iedup - dont worry about alldup #146

iedup - dont worry about alldup #146

Treat as regular dup and let user solve with iecompdup

*! version 5.5 26APR2018 DIME Analytics lcardosodeandrad@worldbank.org

	capture program drop ieduplicates
	program ieduplicates , rclass

	qui {

		syntax varname ,  FOLder(string) UNIQUEvars(varlist) [ KEEPvars(varlist) MINprecision(numlist >0) tostringok droprest nodaily SUFfix(string)]

		version 11.0

		*Add version of Stata fix
		//Make sure that keepvars are still saved if saved if the duplicates file
		*	is generated on a subset of the data. For example, duplicates from
		*	version 1, 2 . If the command is run on only version 1. Then values
		*	for keeepvars in version 2 and 3 are dropped and not reloaded as those
		*	obs are not in current memory

		**Test that observations have not been deleted from the report before readind
		* it. Deleted in a way that the report does not make sense. Provide an error
		* message to this that is more informative.

		preserve

			noi di ""

			** Making one macro with all variables that will be
			*  imported and exported from the Excel file
			local agrumentVars `varlist' `uniquevars' `keepvars'

			** When migrating data from Stata to Excel format, time variables loose precision.
			*  If time varaibles are used for uniquely identifying observations, then this loss
			*  of precision might make a time variable unfit to merge the observations back
			*  to Stata after they have been exported to Excel.
			if "`minprecision'" != "" local milliprecision = `minprecision' * 1000 * 60

			local excelvars dupListID dateListed dateFixed correct drop newID initials notes

			local  date = subinstr(c(current_date)," ","",.)

			/***********************************************************************
			************************************************************************

				Section 1

				Storing format and type of the variables that will be imported and
				exported from the Excel file. The export and import function may
				sometimes alter type and format. Merging with different types results
				in an error and format is required for date and time variabels.
				By using the information stored in this loop, the code can enusre
				that the variables have correct type and format before merging.
				For more information, see below.

			************************************************************************
			***********************************************************************/

			/***********************************************************************
			************************************************************************

				Section 2

				Saving a version of the data to be used before mergin and
				before correcting duplicates

			************************************************************************
			***********************************************************************/

			tempfile restart
			save 	`restart'

			/***********************************************************************
			************************************************************************

				Section 3

				Import and prepare Corrections (if file exists)

			************************************************************************
			***********************************************************************/

			/******************
				Section 3.1
				Check if earlier report exists.
			******************/

			cap confirm file "`folder'/iedupreport`suffix'.xlsx"

			if !_rc {
				local fileExists 1
			}
			else {
				local fileExists 0
			}

			/******************
				Section 3.2
				If report exist, load file and check input
			******************/

			if `fileExists' {

				*Load excel file. Load all vars as string and use meta data from Section 1
				import excel "`folder'/iedupreport`suffix'.xlsx"	, clear firstrow

				** All excelvars but dupListID and newID should be string. dupListID
				*  should be numeric and the type of newID should be based on the user input
				foreach excelvar of local excelvars {
					if !inlist("`excelvar'", "dupListID", "newID") {

						* Make original ID var string
						tostring `excelvar' , replace
						replace  `excelvar' = "" if `excelvar' == "."
					}
				}

				/******************
					Section 3.2.1
					Make sure that the
					ID variable and
					the uniquevars are
					not changed since
					last report.
				******************/

				ds
				local existingexcelvars  `r(varlist)'

				if `:list varlist in existingexcelvars' == 0 {

					noi display as error "{phang}ID variable [`varlist'] does not exist in the previously exported Excle file. If you renamed or changed the ID variable, you need to start over with a new file. Rename or move the already existing file. Create a new file and carefully copy any corrections from the old file to the new.{p_end}"
					noi di ""
					error 111
					exit

				}
				if `:list uniquevars in existingexcelvars' == 0 {

					noi display as error "{phang}One or more unique variables in [`uniquevars'] do not exist in the previously exported Excel file. If you renamed or changed any variable used in uniquevars(), you need to start over with a new file. Rename or move the already existing file. Create a new file and carefully copy any corrections from the old file to the new.{p_end}"
					noi di ""
					error 111
					exit
				}

				/******************
					Section 3.3
					Make sure input is correct
				******************/

				*Temporary variables needed for checking input
				tempvar tmpvar_multiInp tmpvar_inputNotYes tmpvar_maxMultiInp tmpvar_notDrop tempvar_yescorrect tempvar_numcorrect

				*Locals indicating in which ways input is incorrect (if any)
				local local_multiInp 		0
				local local_multiCorr		0
				local local_inputNotYes		0
				local local_notDrop			0

				/******************
					Section 3.3.1
					Make sure there are not too many corrections
				******************/

				* Count the number of corrections (correct drop newID) per
				* observation. Only one correction per observation is allowed.
				egen `tmpvar_multiInp' = rownonmiss(correct drop newID), strok

				*Check that all rows have utmost one correction
				cap assert `tmpvar_multiInp' == 0 | `tmpvar_multiInp' == 1

				if _rc {

					*Error will be outputted below
					local local_multiInp 	1
				}

				/******************
					Section 3.3.2
					Make sure string input is yes or y
				******************/

				* Make string input lower case and change "y" to "yes"
				replace correct = lower(correct)
				replace drop 	= lower(drop)
				replace correct = "yes" if correct 	== "y"
				replace drop 	= "yes" if drop 	== "y"

				*Check that varaibles are wither empty or "yes"
				gen `tmpvar_inputNotYes' = !((correct  == "yes" | correct == "") & (drop  == "yes" | drop == ""))

				cap assert `tmpvar_inputNotYes' == 0
				if _rc {

					*Error will be outputted below
					local local_inputNotYes 	1
				}

				/******************
					Section 3.3.3
					Make sure that either option droprest is specified, or that
					drop was correctly indicated for all observations. i.e.; if
					correct or newID was indicated for at least one duplicate in
					a duplicate group, then all other observations should be
					indicated as drop (unless droprest is specified)

				******************/

				*Check if any other duplicate in duplicate group has at least one correction
				gen `tempvar_yescorrect' = (correct == "yes")
				bys `varlist' : egen `tempvar_numcorrect' =  total(`tempvar_yescorrect')
				count if `tempvar_numcorrect' > 1
				if `r(N)' != 0 local local_multiCorr 1

				*Check if any other duplicate in duplicate group has at least one correction
				bys `varlist' : egen `tmpvar_maxMultiInp' = max(`tmpvar_multiInp')

				*Check that drops are explicitly indicated
				gen `tmpvar_notDrop' = (`tmpvar_multiInp' == 0 & `tmpvar_maxMultiInp' > 0)

				* Check if option droprest is specified
				if "`droprest'" != "" {
					cap assert `tmpvar_notDrop' == 0
					if _rc {

						*Error will be outputted below
						local local_notDrop 	1
					}
				}
				else {

					** Option -droprest- specified. Drop will be changed to yes
					*  for any observations without drop or any other correction
					*  explicitly specified if the observation is in a duplicate
					*  group with at least one observation has a correction
					replace drop 	= "yes" if `tmpvar_notDrop' 	== 1

				}

				/******************
					Section 3.4
					Throw errors if any of the tests were not passed
				******************/

				*Was any error detected
				if `local_multiInp' == 1 | `local_inputNotYes' == 1 | `local_notDrop' == 1 | `local_multiCorr' == 1  {

					*Error multiple input
					if `local_multiInp' == 1 {
						noi {
							display as error "{phang}The following observations have more than one correction. Only one correction (correct, drop or newID) per row is allowed{p_end}"
							list `varlist' dupListID correct drop newID if `tmpvar_multiInp' > 1
							di ""
						}
					}

					*Error multiple correct
					if `local_multiCorr' == 1 {
						noi {
							display as error "{phang}The following observations are in a duplicate group where more than one observation is listed as correct. Only one observation per duplicate group can be correct{p_end}"
							list `varlist' dupListID correct drop newID if `tempvar_numcorrect' > 1
							di ""
						}

					}

					*Error in incorrect string
					if `local_inputNotYes' == 1 {
						noi {
							display as error "{phang}The following observations have an answer in either correct or drop that is neither yes nor y{p_end}"
							list `varlist' dupListID correct drop if `tmpvar_inputNotYes' == 1
							di ""
						}
					}

					*Error is not specfied as drop
					if `local_notDrop' == 1 {
						noi {
							display as error "{phang}The following observations are not explicitly indicated as drop while other duplicates in the same duplicate group are corrected. Either manually indicate as drop or see option droprest{p_end}"
							list `varlist' dupListID correct drop newID if `tmpvar_notDrop' == 1
							di ""
						}
					}

				*Same error for any incorrect input
				error 119
				exit
				}

				*Keep only the variables needed for matching and variables used for input in the Excel file
				keep 	`varlist' `uniquevars' `excelvars'

				*Save imported data set with all corrections
				tempfile imputfile_merge
				save	`imputfile_merge'
			}

			/***********************************************************************
			************************************************************************

				Section 4

				Merge corrections with original data

			************************************************************************
			***********************************************************************/

			*Re-load original
			use `restart', clear

			* Merge original data with imported Excel file (if Excel file exists)
			if `fileExists'  {

				*Create a tempvar for merging results
				tempvar iedup_merge

				*Merge the corrections with the data set
				merge 1:1 `varlist' `uniquevars' using `imputfile_merge', generate(`iedup_merge')

				*Make sure that obsrevations listed in the duplicate report is still in the data set
				cap assert `iedup_merge' != 2

				*Display error message if assertion is not true and some duplicates in the excle file are no longer in the data set
				if _rc {

					display as error "{phang}One or several observations in the Excel report are no longer found in the data set. Always run ieduplicates on the raw data set that include all the duplicates, both new duplicates and those you have already identified. After removing duplicates, save the data set using a different name. You might also recieve this error if you are using an old ieduplicates Excel report on a new data set.{p_end}"
					error 9
					exit

				}

				*Explicitly drop temporary variable. Temporary variables might
				*be exported to excel so delete explicitly before that. Only
				*using tempvar here to create a name with no conflicts
				drop `iedup_merge'

			}

			/***********************************************************************
			************************************************************************

			/***********************************************************************
			************************************************************************

				Section 6

				Test if there are duplicates in ID var. If any duplicates exist,
				tehn update the Excel file with new and unaddressed cases

			************************************************************************
			***********************************************************************/

			/******************
				Section 6.1
				Test if there are any duplicates in ID var
			******************/

			* Generate variable that is not 0
			* if observation is a duplicate
			tempvar dup
			duplicates tag `varlist', gen(`dup')

			*Test if there are any duplicates
			cap assert `dup'==0
			if _rc {

				/******************
					Section 6.2
					Test if the variables passed as ID var and unique var
					uniquely and fully identify the data set. It should be
					possible to merge corrections back to the main file.

				******************/

				cap isid 	`varlist' `uniquevars'
				if _rc {

					display as error "{phang}ID variable `varlist' does not uniquely identify the observations in the data set together with the uniquevars: `uniquevars'. This is needed for exactly merging the right correction to the right observation{p_end}"
					error 119
					exit
				}

				/******************
					Section 6.3
					Keep only duplicates for the report
				******************/

				*Keep if observation is part of duplicate group
				keep if `dup' != 0

				if `fileExists'  {
					* If Excel file exists keep excel vars and
					* variables passed as arguments in the
					* command
					keep 	`agrumentVars' `excelvars'
				}
				else {
					* Keep only variables passed as arguments in
					* the command and the string ID var as no Excel file exists
					keep 	`agrumentVars'

					*Generate the excel variables used for indicating correction
					foreach excelvar of local excelvars {

						*Create all variables apart from dupListID as string vars
						if "`excelvar'" == "dupListID" {
							gen `excelvar' = .
						}
						else {
							gen `excelvar' = ""
						}
					}

				}

				/******************
					Section 6.4
					Update the excel vars that are not updated manually
				******************/

				* Generate a local that is 1 if there are new duplicates
				local unaddressedNewExcel 0
				count if dateFixed == ""
				if `r(N)' > 0 local unaddressedNewExcel 1

				/******************
					Section 6.4.1 Date variables
				******************/

				* Add date first time duplicvate was identified
				replace dateListed 	= "`date'" if dateListed == ""

				** Add today's date to variable dateFixed if dateFixed
				*  is empty and at least one correction is added
				replace dateFixed 	= "`date'" if dateFixed == "" & (correct != "" | drop != "" | newID != "")

				/******************
					Section 6.4.2 Duplicate report list ID
				******************/

				** Sort after dupListID and after ID var for
				*  duplicates currently without dupListID
				sort dupListID `varlist'

				** Assign dupListID 1 to the top row if no duplicate
				*  list IDs have been generated so far.
				replace dupListID = 1 if _n == 1 & dupListID == .

				** Generate new IDs based on the row above instead of directly
				*  from the row number. That prevents duplicates in the list in
				*  case an observation is deleted. The first observation with
				*  missing value will have an ID that is one digit higher than
				*  the highest ID already in the list
				replace dupListID = dupListID[_n - 1] + 1 if dupListID == .

				/******************
					Section 6.5
					Keep and order the variables and output the Excel files
				******************/

				* If cases unaddressed then update the Excel file
				if `unaddressedNewExcel'  {

					keep 	`agrumentVars' `excelvars'
					order	`varlist' `excelvars' `uniquevars' `keepvars'

					if "`daily'" == "" {

						*Returns 0 if folder does not exist, 1 if it does
						mata : st_numscalar("r(dirExist)", direxists("`folder'/Daily"))

						** If the daily folder is not created, just create it
						if `r(dirExist)' == 0  {

							*Create the folder since it does not exist
							mkdir "`folder'/Daily"
						}

						*Export the daily file
						cap export excel using "`folder'/Daily/iedupreport`suffix'_`date'.xlsx"	, firstrow(variables) replace nolabel

						*Print error if daily report cannot be saved
						if _rc {

							display as error "{phang}There the Daily copy could not be saved to the `folder'/Daily folder. Make sure to close any old daily copy or see the option nodaily{p_end}"
							error 603
							exit

						}

						*Prepare local for output
						local daily_output "and a daily copy have been saved to the Daily folder"
					}

					*Export main report
					export excel using "`folder'/iedupreport`suffix'.xlsx"	, firstrow(variables) replace  nolabel

					*Produce output
					noi di `"{phang}Excel file created at: {browse "`folder'/iedupreport`suffix'.xlsx":`folder'/iedupreport`suffix'.xlsx} `daily_output'{p_end}"'
					noi di ""
				}
			}

		/***********************************************************************
		************************************************************************

			Section 7

			Update the data set and with the new corrections.

		************************************************************************
		***********************************************************************/

		* Load the original data set merged with correction. Duplicates
		* in all variables are already dropped in this data set
		use 	`restart', clear

		* If excel file exists, apply any corrections indicated (if any)
		if `fileExists' {

			/******************
				Section 7.1
				Drop duplicates listed for drop
			******************/

			drop if drop == "yes"

			/******************
				Section 7.2
				Update new ID. ID var can be either numeric or
				string. All numbers can be made strings but not
				all strings can be numeric. Therefore this
				section is complicated.
			******************/

			/******************
				Section 7.2.1
				ID var in original file is string. Either
				newID was imported as string or the variable
				is made string. Easy.
			******************/

			*Test if there are any corrections by new ID
			cap assert missing(newID)
			if _rc {

				local idtype 	: type `idvar'
				local idtypeNew : type newID

				*If ID var is string but newID is not, then just make it string
				if substr("`idtype'",1,3) == "str" & substr("`idtypeNew'",1,3) != "str" {

					tostring newID , replace
					replace  newID = "" if newID == "."
				}

				*If ID var is numeric but the newID is loaded as string
				else if substr("`idtype'",1,3) != "str" & substr("`idtypeNew'",1,3) == "str" {

					* Check if -tostringok- is specificed:
					if "`tostringok'" != "" {

						* Make original ID var string
						tostring `idvar' , replace
						replace  `idvar' = "" if `idvar' == "."

					}

					* Error, IDvar can not be updated
					else {

						* Create a local with all non-numeric values
						levelsof newID if missing(real(newID)), local(NaN_values) clean

						* Output error message
						di as error "{phang}The ID varaible `idvar' is numeric but newID has thes non-numeric values: `NaN_values'. Update newID to only contain numeric values or see option tostringok.{p_end}"
						error 109
						exit
					}
				}

				*After making sure that type is ok, update the IDs
				replace `idvar' = newID if !missing(newID)

				/******************
				******************/
					exit
				}
			}

			/******************
				Section 7.4
				Drop Excel vars
			******************/

			drop `excelvars'
		}

		/***********************************************************************
		************************************************************************

			Section 8

			Return the data set without duplicates and
			output information regarding unresolved duplicates.

		************************************************************************
		***********************************************************************/

		* Generate a variable that is 1 if the observation is a duplicate in varlist
		tempvar dropDup
		duplicates tag `varlist',  gen(`dropDup')
		* Generate a list of the IDs that are still duplicates
		levelsof `varlist' 			if `dropDup' != 0 , local(dup_ids) clean
		* Drop the duplicates (they are exported in Excel)
		drop 						if `dropDup' != 0

		* Test if varlist is now uniquely and fully identifying the data set
		cap isid `varlist'
		if _rc {

			di as error "{phang}The data set is not returned with `varlist' uniquely and fully identifying the data set. Please report this bug to kbjarkefur@worldbank.org{p_end}"
			error 119
			exit

		}

		if `:list sizeof dup_ids' == 0 {

			noi di	"{phang}There are no unresolved duplicates in this data set. The data set is returned with `varlist' uniquely and fully identifying the data set.{p_end}"
		}
		else {
			noi di	"{phang}There are `:list sizeof dup_ids' duplicates unresolved. IDs still contining duplicates: `dup_ids'. The unresolved duplicate observations were exported in the Excel file. The data set is returned without those duplicates and with `varlist' uniquely and fully identifying the data set.{p_end}"
		}

		return scalar numDup	= `:list sizeof dup_ids'

		/***********************************************************************
		************************************************************************

			Section 9

			Save data set to be returned outside preserve/restore.
			Preserve/restore is used so that original data is returned
			in case an error is thrown.

		************************************************************************
		***********************************************************************/

		tempfile returndata
		save 	`returndata'

		restore

		** Using restore above to return the data to
		*  the orignal data set in case of error.
		use `returndata', clear

	}
	end

*! version 5.5 26APR2018 DIME Analytics lcardosodeandrad@worldbank.org

	capture program drop ieduplicates
	program ieduplicates , rclass

	qui {

		syntax varname ,  FOLder(string) UNIQUEvars(varlist) [ KEEPvars(varlist) MINprecision(numlist >0) tostringok droprest nodaily SUFfix(string)]

		version 11.0

		*Add version of Stata fix
		//Make sure that keepvars are still saved if saved if the duplicates file
		*	is generated on a subset of the data. For example, duplicates from
		*	version 1, 2 . If the command is run on only version 1. Then values
		*	for keeepvars in version 2 and 3 are dropped and not reloaded as those
		*	obs are not in current memory

		**Test that observations have not been deleted from the report before readind
		* it. Deleted in a way that the report does not make sense. Provide an error
		* message to this that is more informative.

		preserve

			noi di ""

			** Making one macro with all variables that will be
			*  imported and exported from the Excel file
			local agrumentVars `varlist' `uniquevars' `keepvars'

			** When migrating data from Stata to Excel format, time variables loose precision.
			*  If time varaibles are used for uniquely identifying observations, then this loss
			*  of precision might make a time variable unfit to merge the observations back
			*  to Stata after they have been exported to Excel.
			if "`minprecision'" != "" local milliprecision = `minprecision' * 1000 * 60

			local excelvars dupListID dateListed dateFixed correct drop newID initials notes

			local  date = subinstr(c(current_date)," ","",.)

			/***********************************************************************
			************************************************************************

				Section 1

				Storing format and type of the variables that will be imported and
				exported from the Excel file. The export and import function may
				sometimes alter type and format. Merging with different types results
				in an error and format is required for date and time variabels.
				By using the information stored in this loop, the code can enusre
				that the variables have correct type and format before merging.
				For more information, see below.

			************************************************************************
			***********************************************************************/

			/***********************************************************************
			************************************************************************

				Section 2

				Saving a version of the data to be used before mergin and
				before correcting duplicates

			************************************************************************
			***********************************************************************/

			tempfile restart
			save 	`restart'

			/***********************************************************************
			************************************************************************

				Section 3

				Import and prepare Corrections (if file exists)

			************************************************************************
			***********************************************************************/

			/******************
				Section 3.1
				Check if earlier report exists.
			******************/

			cap confirm file "`folder'/iedupreport`suffix'.xlsx"

			if !_rc {
				local fileExists 1
			}
			else {
				local fileExists 0
			}

			/******************
				Section 3.2
				If report exist, load file and check input
			******************/

			if `fileExists' {

				*Load excel file. Load all vars as string and use meta data from Section 1
				import excel "`folder'/iedupreport`suffix'.xlsx"	, clear firstrow

				** All excelvars but dupListID and newID should be string. dupListID
				*  should be numeric and the type of newID should be based on the user input
				foreach excelvar of local excelvars {
					if !inlist("`excelvar'", "dupListID", "newID") {

						* Make original ID var string
						tostring `excelvar' , replace
						replace  `excelvar' = "" if `excelvar' == "."
					}
				}

				/******************
					Section 3.2.1
					Make sure that the
					ID variable and
					the uniquevars are
					not changed since
					last report.
				******************/

				ds
				local existingexcelvars  `r(varlist)'

				if `:list varlist in existingexcelvars' == 0 {

					noi display as error "{phang}ID variable [`varlist'] does not exist in the previously exported Excle file. If you renamed or changed the ID variable, you need to start over with a new file. Rename or move the already existing file. Create a new file and carefully copy any corrections from the old file to the new.{p_end}"
					noi di ""
					error 111
					exit

				}
				if `:list uniquevars in existingexcelvars' == 0 {

					noi display as error "{phang}One or more unique variables in [`uniquevars'] do not exist in the previously exported Excel file. If you renamed or changed any variable used in uniquevars(), you need to start over with a new file. Rename or move the already existing file. Create a new file and carefully copy any corrections from the old file to the new.{p_end}"
					noi di ""
					error 111
					exit
				}

				/******************
					Section 3.3
					Make sure input is correct
				******************/

				*Temporary variables needed for checking input
				tempvar tmpvar_multiInp tmpvar_inputNotYes tmpvar_maxMultiInp tmpvar_notDrop tempvar_yescorrect tempvar_numcorrect

				*Locals indicating in which ways input is incorrect (if any)
				local local_multiInp 		0
				local local_multiCorr		0
				local local_inputNotYes		0
				local local_notDrop			0

				/******************
					Section 3.3.1
					Make sure there are not too many corrections
				******************/

				* Count the number of corrections (correct drop newID) per
				* observation. Only one correction per observation is allowed.
				egen `tmpvar_multiInp' = rownonmiss(correct drop newID), strok

				*Check that all rows have utmost one correction
				cap assert `tmpvar_multiInp' == 0 | `tmpvar_multiInp' == 1

				if _rc {

					*Error will be outputted below
					local local_multiInp 	1
				}

				/******************
					Section 3.3.2
					Make sure string input is yes or y
				******************/

				* Make string input lower case and change "y" to "yes"
				replace correct = lower(correct)
				replace drop 	= lower(drop)
				replace correct = "yes" if correct 	== "y"
				replace drop 	= "yes" if drop 	== "y"

				*Check that varaibles are wither empty or "yes"
				gen `tmpvar_inputNotYes' = !((correct  == "yes" | correct == "") & (drop  == "yes" | drop == ""))

				cap assert `tmpvar_inputNotYes' == 0
				if _rc {

					*Error will be outputted below
					local local_inputNotYes 	1
				}

				/******************
					Section 3.3.3
					Make sure that either option droprest is specified, or that
					drop was correctly indicated for all observations. i.e.; if
					correct or newID was indicated for at least one duplicate in
					a duplicate group, then all other observations should be
					indicated as drop (unless droprest is specified)

				******************/

				*Check if any other duplicate in duplicate group has at least one correction
				gen `tempvar_yescorrect' = (correct == "yes")
				bys `varlist' : egen `tempvar_numcorrect' =  total(`tempvar_yescorrect')
				count if `tempvar_numcorrect' > 1
				if `r(N)' != 0 local local_multiCorr 1

				*Check if any other duplicate in duplicate group has at least one correction
				bys `varlist' : egen `tmpvar_maxMultiInp' = max(`tmpvar_multiInp')

				*Check that drops are explicitly indicated
				gen `tmpvar_notDrop' = (`tmpvar_multiInp' == 0 & `tmpvar_maxMultiInp' > 0)

				* Check if option droprest is specified
				if "`droprest'" != "" {
					cap assert `tmpvar_notDrop' == 0
					if _rc {

						*Error will be outputted below
						local local_notDrop 	1
					}
				}
				else {

					** Option -droprest- specified. Drop will be changed to yes
					*  for any observations without drop or any other correction
					*  explicitly specified if the observation is in a duplicate
					*  group with at least one observation has a correction
					replace drop 	= "yes" if `tmpvar_notDrop' 	== 1

				}

				/******************
					Section 3.4
					Throw errors if any of the tests were not passed
				******************/

				*Was any error detected
				if `local_multiInp' == 1 | `local_inputNotYes' == 1 | `local_notDrop' == 1 | `local_multiCorr' == 1  {

					*Error multiple input
					if `local_multiInp' == 1 {
						noi {
							display as error "{phang}The following observations have more than one correction. Only one correction (correct, drop or newID) per row is allowed{p_end}"
							list `varlist' dupListID correct drop newID if `tmpvar_multiInp' > 1
							di ""
						}
					}

					*Error multiple correct
					if `local_multiCorr' == 1 {
						noi {
							display as error "{phang}The following observations are in a duplicate group where more than one observation is listed as correct. Only one observation per duplicate group can be correct{p_end}"
							list `varlist' dupListID correct drop newID if `tempvar_numcorrect' > 1
							di ""
						}

					}

					*Error in incorrect string
					if `local_inputNotYes' == 1 {
						noi {
							display as error "{phang}The following observations have an answer in either correct or drop that is neither yes nor y{p_end}"
							list `varlist' dupListID correct drop if `tmpvar_inputNotYes' == 1
							di ""
						}
					}

					*Error is not specfied as drop
					if `local_notDrop' == 1 {
						noi {
							display as error "{phang}The following observations are not explicitly indicated as drop while other duplicates in the same duplicate group are corrected. Either manually indicate as drop or see option droprest{p_end}"
							list `varlist' dupListID correct drop newID if `tmpvar_notDrop' == 1
							di ""
						}
					}

				*Same error for any incorrect input
				error 119
				exit
				}

				*Keep only the variables needed for matching and variables used for input in the Excel file
				keep 	`varlist' `uniquevars' `excelvars'

				*Save imported data set with all corrections
				tempfile imputfile_merge
				save	`imputfile_merge'
			}

			/***********************************************************************
			************************************************************************

				Section 4

				Merge corrections with original data

			************************************************************************
			***********************************************************************/

			*Re-load original
			use `restart', clear

			* Merge original data with imported Excel file (if Excel file exists)
			if `fileExists'  {

				*Create a tempvar for merging results
				tempvar iedup_merge

				*Merge the corrections with the data set
				merge 1:1 `varlist' `uniquevars' using `imputfile_merge', generate(`iedup_merge')

				*Make sure that obsrevations listed in the duplicate report is still in the data set
				cap assert `iedup_merge' != 2

				*Display error message if assertion is not true and some duplicates in the excle file are no longer in the data set
				if _rc {

					display as error "{phang}One or several observations in the Excel report are no longer found in the data set. Always run ieduplicates on the raw data set that include all the duplicates, both new duplicates and those you have already identified. After removing duplicates, save the data set using a different name. You might also recieve this error if you are using an old ieduplicates Excel report on a new data set.{p_end}"
					error 9
					exit

				}

				*Explicitly drop temporary variable. Temporary variables might
				*be exported to excel so delete explicitly before that. Only
				*using tempvar here to create a name with no conflicts
				drop `iedup_merge'

			}

			/***********************************************************************
			************************************************************************

				Section 5

				Drop all but one observations that are duplicates in all variables

			************************************************************************
			***********************************************************************/

			tempvar id_string allDup

			/******************
				Section 5.2
				Throw errors if any of the tests was not passed
			******************/

			** Generate variables that are not 0 if any observations are
			*  duplicates in all variables
			duplicates tag , gen(`allDup')

			*Test if any observations is duplicates in all variables
			count if `allDup' != 0
			if `r(N)' != 0 {

				* Output message indicating that some observations
				* were dropped automatically as they were duplicates
				* in all variables.
				noi di "{phang}The following IDs are duplicates in all variable so only one version is kept. The other observations in the same duplicate group are automatically dropped:{p_end}"

				*Create a local of all IDs that are to be deleted.
				levelsof `id_string' if `allDup' != 0
				foreach alldupID in `r(levels)' {

					*Output the ID
					noi di "{phang2}ID: `alldupID'{p_end}"

					*Drop all but one duplicates in duplicate
					*groups that are duplicated in all variables
					duplicates drop 	if `id_string' == "`alldupID'"
				}

				*Add an empty row after the output
				noi di ""
			}

			** Save data set including dropping duplicates in all variables.
			*  The command returns the data set without these observations.
			save `restart', replace

			/***********************************************************************
			************************************************************************

				Section 6

				Test if there are duplicates in ID var. If any duplicates exist,
				tehn update the Excel file with new and unaddressed cases

			************************************************************************
			***********************************************************************/

			/******************
				Section 6.1
				Test if there are any duplicates in ID var
			******************/

			* Generate variable that is not 0
			* if observation is a duplicate
			tempvar dup
			duplicates tag `varlist', gen(`dup')

			*Test if there are any duplicates
			cap assert `dup'==0
			if _rc {

				/******************
					Section 6.2
					Test if the variables passed as ID var and unique var
					uniquely and fully identify the data set. It should be
					possible to merge corrections back to the main file.

				******************/

				cap isid 	`varlist' `uniquevars'
				if _rc {

					display as error "{phang}ID variable `varlist' does not uniquely identify the observations in the data set together with the uniquevars: `uniquevars'. This is needed for exactly merging the right correction to the right observation{p_end}"
					error 119
					exit
				}

				/******************
					Section 6.3
					Keep only duplicates for the report
				******************/

				*Keep if observation is part of duplicate group
				keep if `dup' != 0

				if `fileExists'  {
					* If Excel file exists keep excel vars and
					* variables passed as arguments in the
					* command
					keep 	`agrumentVars' `excelvars'
				}
				else {
					* Keep only variables passed as arguments in
					* the command and the string ID var as no Excel file exists
					keep 	`agrumentVars'

					*Generate the excel variables used for indicating correction
					foreach excelvar of local excelvars {

						*Create all variables apart from dupListID as string vars
						if "`excelvar'" == "dupListID" {
							gen `excelvar' = .
						}
						else {
							gen `excelvar' = ""
						}
					}

				}

				/******************
					Section 6.4
					Update the excel vars that are not updated manually
				******************/

				* Generate a local that is 1 if there are new duplicates
				local unaddressedNewExcel 0
				count if dateFixed == ""
				if `r(N)' > 0 local unaddressedNewExcel 1

				/******************
					Section 6.4.1 Date variables
				******************/

				* Add date first time duplicvate was identified
				replace dateListed 	= "`date'" if dateListed == ""

				** Add today's date to variable dateFixed if dateFixed
				*  is empty and at least one correction is added
				replace dateFixed 	= "`date'" if dateFixed == "" & (correct != "" | drop != "" | newID != "")

				/******************
					Section 6.4.2 Duplicate report list ID
				******************/

				** Sort after dupListID and after ID var for
				*  duplicates currently without dupListID
				sort dupListID `varlist'

				** Assign dupListID 1 to the top row if no duplicate
				*  list IDs have been generated so far.
				replace dupListID = 1 if _n == 1 & dupListID == .

				** Generate new IDs based on the row above instead of directly
				*  from the row number. That prevents duplicates in the list in
				*  case an observation is deleted. The first observation with
				*  missing value will have an ID that is one digit higher than
				*  the highest ID already in the list
				replace dupListID = dupListID[_n - 1] + 1 if dupListID == .

				/******************
					Section 6.5
					Keep and order the variables and output the Excel files
				******************/

				* If cases unaddressed then update the Excel file
				if `unaddressedNewExcel'  {

					keep 	`agrumentVars' `excelvars'
					order	`varlist' `excelvars' `uniquevars' `keepvars'

					if "`daily'" == "" {

						*Returns 0 if folder does not exist, 1 if it does
						mata : st_numscalar("r(dirExist)", direxists("`folder'/Daily"))

						** If the daily folder is not created, just create it
						if `r(dirExist)' == 0  {

							*Create the folder since it does not exist
							mkdir "`folder'/Daily"
						}

						*Export the daily file
						cap export excel using "`folder'/Daily/iedupreport`suffix'_`date'.xlsx"	, firstrow(variables) replace nolabel

						*Print error if daily report cannot be saved
						if _rc {

							display as error "{phang}There the Daily copy could not be saved to the `folder'/Daily folder. Make sure to close any old daily copy or see the option nodaily{p_end}"
							error 603
							exit

						}

						*Prepare local for output
						local daily_output "and a daily copy have been saved to the Daily folder"
					}

					*Export main report
					export excel using "`folder'/iedupreport`suffix'.xlsx"	, firstrow(variables) replace  nolabel

					*Produce output
					noi di `"{phang}Excel file created at: {browse "`folder'/iedupreport`suffix'.xlsx":`folder'/iedupreport`suffix'.xlsx} `daily_output'{p_end}"'
					noi di ""
				}
			}

		/***********************************************************************
		************************************************************************

			Section 7

			Update the data set and with the new corrections.

		************************************************************************
		***********************************************************************/

		* Load the original data set merged with correction. Duplicates
		* in all variables are already dropped in this data set
		use 	`restart', clear

		* If excel file exists, apply any corrections indicated (if any)
		if `fileExists' {

			/******************
				Section 7.1
				Drop duplicates listed for drop
			******************/

			drop if drop == "yes"

			/******************
				Section 7.2
				Update new ID. ID var can be either numeric or
				string. All numbers can be made strings but not
				all strings can be numeric. Therefore this
				section is complicated.
			******************/

			/******************
				Section 7.2.1
				ID var in original file is string. Either
				newID was imported as string or the variable
				is made string. Easy.
			******************/

			*Test if there are any corrections by new ID
			cap assert missing(newID)
			if _rc {

				local idtype 	: type `idvar'
				local idtypeNew : type newID

				*If ID var is string but newID is not, then just make it string
				if substr("`idtype'",1,3) == "str" & substr("`idtypeNew'",1,3) != "str" {

					tostring newID , replace
					replace  newID = "" if newID == "."
				}

				*If ID var is numeric but the newID is loaded as string
				else if substr("`idtype'",1,3) != "str" & substr("`idtypeNew'",1,3) == "str" {

					* Check if -tostringok- is specificed:
					if "`tostringok'" != "" {

						* Make original ID var string
						tostring `idvar' , replace
						replace  `idvar' = "" if `idvar' == "."

					}

					* Error, IDvar can not be updated
					else {

						* Create a local with all non-numeric values
						levelsof newID if missing(real(newID)), local(NaN_values) clean

						* Output error message
						di as error "{phang}The ID varaible `idvar' is numeric but newID has thes non-numeric values: `NaN_values'. Update newID to only contain numeric values or see option tostringok.{p_end}"
						error 109
						exit
					}
				}

				*After making sure that type is ok, update the IDs
				replace `idvar' = newID if !missing(newID)

				/******************
				******************/
					exit
				}
			}

			/******************
				Section 7.4
				Drop Excel vars
			******************/

			drop `excelvars'
		}

		/***********************************************************************
		************************************************************************

			Section 8

			Return the data set without duplicates and
			output information regarding unresolved duplicates.

		************************************************************************
		***********************************************************************/

		* Generate a variable that is 1 if the observation is a duplicate in varlist
		tempvar dropDup
		duplicates tag `varlist',  gen(`dropDup')
		* Generate a list of the IDs that are still duplicates
		levelsof `varlist' 			if `dropDup' != 0 , local(dup_ids) clean
		* Drop the duplicates (they are exported in Excel)
		drop 						if `dropDup' != 0

		* Test if varlist is now uniquely and fully identifying the data set
		cap isid `varlist'
		if _rc {

			di as error "{phang}The data set is not returned with `varlist' uniquely and fully identifying the data set. Please report this bug to kbjarkefur@worldbank.org{p_end}"
			error 119
			exit

		}

		if `:list sizeof dup_ids' == 0 {

			noi di	"{phang}There are no unresolved duplicates in this data set. The data set is returned with `varlist' uniquely and fully identifying the data set.{p_end}"
		}
		else {
			noi di	"{phang}There are `:list sizeof dup_ids' duplicates unresolved. IDs still contining duplicates: `dup_ids'. The unresolved duplicate observations were exported in the Excel file. The data set is returned without those duplicates and with `varlist' uniquely and fully identifying the data set.{p_end}"
		}

		return scalar numDup	= `:list sizeof dup_ids'

		/***********************************************************************
		************************************************************************

			Section 9

			Save data set to be returned outside preserve/restore.
			Preserve/restore is used so that original data is returned
			in case an error is thrown.

		************************************************************************
		***********************************************************************/

		tempfile returndata
		save 	`returndata'

		restore

		** Using restore above to return the data to
		*  the orignal data set in case of error.
		use `returndata', clear

	}
	end


added titles to crossreg.ado

added titles to crossreg.ado

clear all
set more off
set matsize 11000
program drop _all

qui adopath + "/mnt/nfs6/wikipedia.proj/jmp/jmpscripts/stata/ado"
cd ${path}

// this declares global vars
declare_global

// 0. Data

// 0.1 make cleancnty, changesets data
clear
preparebasic

// 0.2 make relevant dependent variables
use ${stash}cleanchangeset1, clear
makedv fips
balancepanel fips
mergebasic
qui destring, replace
save ${stash}panelfips, replace

// 1. Summary Stats
program drop _all
use ${stash}panelfips, clear
makesummary

// 2. Treatment Control Balance
use ${stash}panelfips, clear
makehist cntypop kdensity "Population"
makehist emp_earnings kdensity "Earnings"
makehist age_median kdensity "Median Age"
makehist educ_college kdensity "Num. College Educated"
** makehist region hist

// 2. TODO: TTest or regressions treatment / control

//  3.1 Meanline charts (raw data)
makemeanline numcontrib quarter 2011 "Contributions"
makemeanline numuser quarter 2011 "Users"
makemeanline numserious90 quarter 2011 "Super Users"

* makemeanline numnewusers quarter 2011 "New Users"
* makemeanline numnewusers6 quarter 2011 "New Users (Stay for 6+ Months)"
* makemeanline numnewusers90 quarter 2011 "New Users (who become super users)"
* makemeanline numchanges quarter 2011 "Changes"

// 3.2 Cross Sectional Regressions
crossreg

// 3.3 Diff in Diff --- Baseline
local outcomes "numcontrib numuser numserious90"
dd_simple "`outcomes'" run xtreg
dd_simple "`outcomes'" run xtpoisson
dd_simple "`outcomes'" write ddsimple_baseline

// 3.4 Diff in Diff --- Additional
local outcomes "numnewusers numnewusers6 numnewusers90"
dd_simple "`outcomes'" run xtreg
dd_simple "`outcomes'" run xtpoisson
dd_simple "`outcomes'" write ddsimple_additional

// 3.5 Diff in Diff Picture
ddchart numuser
ddchart numcontrib
ddchart numserious90

// 4. How do results vary?
// takes a while
makehetero

// 5. Individual Level Regressions
makeperson
ddperson maketables

ddperson makechart

////////// OLD //
 ** Analysis

// 0. Data

// 0.1 make fips dataset
use ${stash}mergemaster1, clear

balancepanel fips
save ${stash}panelfips, replace

// 0.2 make node dataset
preparenode

use ${stash}mergemaster_node, clear
makedv fips node
balancepanel fips node
save ${stash}panelnode, replace

// 0.3 make way dataset
prepareway
makedv fips way
balancepanel fips way
save ${stash}panelway, replace

// 3. Baseline effects (xtpoisson, cluster at unit level)

// 3.2 FIPS Sample -- DD

/// 3.2.1 -- contrib, users, superu
program drop _all

clear
local dv "numcontrib numuser numserious90"
local unit fips
rundd panelfips `unit' "`dv'"
diffindiff xtpoisson "`dv'" `unit' 2014 write tab_3.2.1 "Contributions" "Users" "Super Users"

/// 3.2.2 -- newusers, newusers6, newuserssuper
clear
local dv "numnewusers numnewusers6 numnewusers90"
local unit fips
rundd panelfips `unit' "`dv'"
diffindiff xtpoisson "`dv'" `unit' 2014 write tab_3.2.2 "New Users" "New Users(6+)" "New Super Users"

/// 3.2.3 -- streetcontrib, nonstreet contrib, amenities
// TODO

use ${stash}panelnode, clear
program drop _all

diffindiff xtpoisson fips run tab_test numcontrib numuser

/// 3.2.4 -- completeness
// TODO

// 3.2.5 -- Diff in Diff charts
// TODO : make this more flexible
// TODO : this works only for fips
ddchart

// 3.3 Robustness : Drop empty counties
// TODO
//  e. Repeat (a) by dropping empty counties
//  f. Repeat (a) by dropping west
//  g. Repeat (a) for Tile Sample, MSA sample
//  h. Repeat (a) with matched estimators
//  i. Repeat (a) with spatially clustered se, Region X Time trends

// 4 Heterogenous Effects

// 1. Large vs. small
use ${stash}panelfips, clear
gen large = (cntypop > 100000)

diffindiff2 large LARGE tab_4.1 run numcontrib numuser numserious90 numnewusers6

diffindiff2 large LARGE tab_4.1 write numcontrib numuser numserious90 numnewusers6

// 2. engaged vs. non-engaged county

use ${stash}panelfips, clear

bysort fips post: egen tmp = total(numcontrib > 0)
replace tmp = . if post == 1
bysort fips: egen precontrib = max(tmp)
bysort fips: egen totcontrib = total(numcontrib)
drop tmp

drop if precontrib == 0
gen active = (prec > 1)

diffindiff2 active ACTIVE tab_4.2 run numcontrib numuser numserious90 numnewusers6

diffindiff2 active ACTIVE tab_4.2 write numcontrib numuser numserious90 numnewusers6

// 3. populated vs. not

use ${stash}panelfips, clear

gen iseast = (region==1 | region == 2)

diffindiff2 iseast EAST tab_4.3 run numcontrib numuser numserious90 numnewusers6

diffindiff2 iseast EAST tab_4.3 write numcontrib numuser numserious90 numnewusers6

// 3. urban vs rural
// 4. university vs non university
// 5. rich vs poor

// 5 Street Level Regressions

// 6 Person Level Regressions

// [A] outcome vars: County sample -- DD spec

   // a. Contributions
   // b. Street, Non Street
   // b. TIGER vs. Non Tiger
   // d. Home county, Num Counties

// [B] heterogenous

   // a. Junior vs Senior

// 7 Impacts on Craigslist

preparecl

// PENDING

// GEOID Level

// TASKS for tomorrow

// STASH

 // 1.1 Mean Charts

 // 1.2 Produce Diff in diff Latex tables
 // rundd -> batchreg.sh -> runreg.do -> diffindiff.ado
 program drop _all

 // TODO: fix manual process
 // have to do this manually

 // 1.3 Produce Diff in diff Pictures

 ** person level regressions
 // person level

// TODOs for Tuesday Jun 3
// GEOID dataset
// TILEID diff in diff
// What happened to previous people?
// What happened at the street/amenity level?
// Urban vs Rural (framework for third level diff)

// 1.4 Robust: product diff in diff for GEOID

use ${stash}panelfips_geoid10, clear

** Analysis Stage 2
//

// PROGRAMS


* site title no longer empty

* site title no longer empty

# ensure audio file has been created
redo-ifchange $1.oga

ALBUM=`vorbiscomment -l $1.oga|grep --only-matching --perl-regexp '(?<=^album=).*$'`
DATE=`vorbiscomment -l $1.oga|grep --only-matching --perl-regexp '(?<=^date=).*$'`
DESCRIPTION=`vorbiscomment -l $1.oga|grep --only-matching --perl-regexp '(?<=^description=).*$'`
TRACKNUMBER=`vorbiscomment -l $1.oga|grep --only-matching --perl-regexp '(?<=^tracknumber=).*$'`

cat << EOF >> $3
<!DOCTYPE html>
<meta charset=utf-8>
<title>$ALBUM – Folge $TRACKNUMBER</title>
<link rel=stylesheet href=style.css>

<hgroup>

$ALBUM

Folge $TRACKNUMBER vom `date +%-d.%-m.%Y -d$DATE`

</hgroup>

    <audio controls>
        <source src="$1.oga">
        Download: $1.oga
    </audio>

        $DESCRIPTION

Linkliste

EOF

cat $1.linklist >> $3

cat << EOF >> $3

    Footertext.

EOF

# ensure audio file has been created
redo-ifchange $1.oga

ALBUM=`vorbiscomment -l $1.oga|grep --only-matching --perl-regexp '(?<=^album=).*$'`
DATE=`vorbiscomment -l $1.oga|grep --only-matching --perl-regexp '(?<=^date=).*$'`
DESCRIPTION=`vorbiscomment -l $1.oga|grep --only-matching --perl-regexp '(?<=^description=).*$'`
TRACKNUMBER=`vorbiscomment -l $1.oga|grep --only-matching --perl-regexp '(?<=^tracknumber=).*$'`

cat << EOF >> $3
<!DOCTYPE html>
<meta charset=utf-8>
<title>$TITLE</title>
<link rel=stylesheet href=style.css>

<hgroup>

$ALBUM

Folge $TRACKNUMBER vom `date +%-d.%-m.%Y -d$DATE`

</hgroup>

    <audio controls>
        <source src="$1.oga">
        Download: $1.oga
    </audio>

        $DESCRIPTION

Linkliste

EOF

cat $1.linklist >> $3

cat << EOF >> $3

    Footertext.

EOF


Build the emulator unconditionally

Build the emulator unconditionally

# Allows building ROMs even if bin isn't in path.
export PATH=$PATH:$(pwd)/bin

redo-ifchange bin/e

redo-ifchange roms/mlm
redo-ifchange roms/ngaro
redo-ifchange roms/sdtest

echo "Be sure to add bin/ to your \$PATH if you haven't already." >&2
echo "  export PATH=\$PATH:$(pwd)/bin" >&2

# Allows building ROMs even if bin isn't in path.
export PATH=$PATH:$(pwd)/bin

# redo-ifchange bin/a
# redo-ifchange bin/bspl.fs
# redo-ifchange bin/e

redo-ifchange roms/mlm
redo-ifchange roms/ngaro
redo-ifchange roms/sdtest

echo "Be sure to add bin/ to your \$PATH if you haven't already." >&2
echo "  export PATH=\$PATH:$(pwd)/bin" >&2


Make default rep for -parallel bs- the same as for -bs-

*! parallel_bs vers 0.14.6.24 24jun2014
*! auth George G. Vega

program def parallel_bs, eclass

	vers 11.0

	if !replay() { // If it is a replay,
		parallel_bootstrap `0'
	}
	else if ("`e(prefix)'" != "bootstrap" | !inlist("`e(pll)'","1") ) { // If the last command runned wasn't nnmatch2
		di as error "Last estimation was not {bf:parallel bs}, was -{bf:`=e(prefix)'}-"
		exit 301
	}
	else { // if the last command was pll bootstrap, it replays the results
		bs, title(parallel bootstrapping)
	}
end

program def parallel_bootstrap, rclass

	vers 11.0

	#delimit ;
	syntax anything(name=model equalok everything) [,
		EXPression(string asis)
		PROGrams(string)
		Mata
		NOGlobals
		Seeds(passthru)
		Randtype(passthru)
		Timeout(integer 60)
		PROCessors(integer 0)
		argopt(string)
		SAVing(string) Reps(integer 50) Keep KEEPLast *];
	#delimit cr

	/* Checking whereas parallel has been config */
	if length("$PLL_CLUSTERS") == 0 {
		di "{error:You haven't set the number of clusters}" _n "{error:Please set it with: {cmd:parallel setclusters} {it:#}}"
		exit 198
	}

	/* Setting sizes */
	//BS needs normally reps at least 2 (per cluster)
	if `reps'<2*$PLL_CLUSTERS {
		_assert `reps'>1, msg("reps() must be an integer greater than 1") rc(198)

		local orig_PLL_CLUSTERS = ${PLL_CLUSTERS}
		global PLL_CLUSTERS = floor(`reps'/2)
		di "Small workload. Temporarily setting number of clusters to ${PLL_CLUSTERS}"
	}
	local csize = floor(`reps'/$PLL_CLUSTERS)
	local lsize = `csize' + (`reps' - `csize'*$PLL_CLUSTERS)

	/* Reserving a pll_id. This will be stored in the -parallelid- local
	macro */
	mata: parallel_sandbox(5)

	/* Saving the tmpfile */
	local tmpdta = "__pll`parallelid'_bs_dta.dta"
	if (`"`saving'"' == "") {
		if (c(os)=="Windows") local saving = `"`c(tmpdir)'__pll`parallelid'_bs_outdta.dta"'
		else local saving = `"`c(tmpdir)'/__pll`parallelid'_bs_outdta.dta"'
		local save = 0
	}
	else local save = 1
	local simul = `"__pll`parallelid'_bs_simul.do"'

	qui save `tmpdta'

	/* Parsing saving */
	_prefix_saving `saving'
	local saving    `"`s(filename)'"'
	if "`double'" == "" {
			local double    `"`s(double)'"'
	}
	local every     `"`s(every)'"'
	local replace   `"`s(replace)'"'

	cap confirm file `saving'
	if (!_rc & "`replace'" == "") {
		di "{error:File -`saving'- already exists, use the -replace- option}"
		if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'
		exit 602
	}

	/* Parsing a program */
	if (regexm(`"`model'"', "^([a-zA-Z0-9_]+)")) local cmd = regexs(1)
	cap findfile `cmd'.ado
	if (_rc) local programs `programs' `cmd'

	/* Creating a tmp program */
	tempname fh
	cap file open `fh' using `"`simul'"', w replace
	file write `fh' `"use `tmpdta', clear"' _n
	file write `fh' "if (\`pll_instance'==\$PLL_CLUSTERS) local reps = `lsize'" _n
	file write `fh' "else local reps = `csize'" _n
	file write `fh' `"local pll_instance : di %04.0f \`pll_instance'"' _n
	file write `fh' `"bs `expression', sav(__pll\`pll_id'_bs_eststore\`pll_instance', replace `double' `every') `options' rep(\`reps'): `model' `argopt'"' _n
	file close `fh'

	/* Running parallel */
	cap noi parallel do `simul', nodata programs(`programs') `mata' `noglobals' `seeds' ///
		`randtype' timeout(`timeout') processors(`processors') setparallelid(`parallelid') `keep' `keeplast'
	local pllseeds = r(pll_seeds)

	if (_rc) {
		if ("`keep'"=="" & "`keeplast'"=="") qui parallel clean, e(${LAST_PLL_ID}) force nologs
		mata: parallel_sandbox(2, "`parallelid'")
		if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'
		exit _rc
	}

	preserve

	/* Appending datasets */
	forval i=1/$PLL_CLUSTERS {
		quietly {
			local pll_instance : di %04.0f `i'
			use `"__pll$LAST_PLL_ID`'_bs_eststore`pll_instance'"', clear

			if ((`=`i'-1')) append using `saving'
			save `saving', replace
		}
	}

	/* Storing macros */
	local macros : r(macros)
	local scalars : r(scalars)
	foreach m of local macros {
		local `m' = r(`m')
	}
	foreach s of local scalars {
		local `s' = r(`s')
	}

	restore

	/* Returning bs data */
	bstat using `saving', title(parallel bootstrapping)

	/* Cleaning up */
	if ("`keep'"=="" & "`keeplast'"=="") parallel clean, e(${LAST_PLL_ID}) nologs
	mata: parallel_sandbox(2, "`parallelid'")

	parallel_bs_ereturn

	/* Getting macros back */
	if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'
	foreach m of local macros {
		return local `m'  `"``m''"'
	}
	foreach s of local scalars {
		return scalar `s' = ``s''
	}
	return local pll_seeds = "`pllseeds'"

end

program def parallel_bs_ereturn, eclass
	vers 11.0
	ereturn local pll 1
end

*! parallel_bs vers 0.14.6.24 24jun2014
*! auth George G. Vega

program def parallel_bs, eclass

	vers 11.0

	if !replay() { // If it is a replay,
		parallel_bootstrap `0'
	}
	else if ("`e(prefix)'" != "bootstrap" | !inlist("`e(pll)'","1") ) { // If the last command runned wasn't nnmatch2
		di as error "Last estimation was not {bf:parallel bs}, was -{bf:`=e(prefix)'}-"
		exit 301
	}
	else { // if the last command was pll bootstrap, it replays the results
		bs, title(parallel bootstrapping)
	}
end

program def parallel_bootstrap, rclass

	vers 11.0

	#delimit ;
	syntax anything(name=model equalok everything) [,
		EXPression(string asis)
		PROGrams(string)
		Mata
		NOGlobals
		Seeds(passthru)
		Randtype(passthru)
		Timeout(integer 60)
		PROCessors(integer 0)
		argopt(string)
		SAVing(string) Reps(integer 100) Keep KEEPLast *];
	#delimit cr

	/* Checking whereas parallel has been config */
	if length("$PLL_CLUSTERS") == 0 {
		di "{error:You haven't set the number of clusters}" _n "{error:Please set it with: {cmd:parallel setclusters} {it:#}}"
		exit 198
	}

	/* Setting sizes */
	//BS needs normally reps at least 2 (per cluster)
	if `reps'<2*$PLL_CLUSTERS {
		_assert `reps'>1, msg("reps() must be an integer greater than 1") rc(198)

		local orig_PLL_CLUSTERS = ${PLL_CLUSTERS}
		global PLL_CLUSTERS = floor(`reps'/2)
		di "Small workload. Temporarily setting number of clusters to ${PLL_CLUSTERS}"
	}
	local csize = floor(`reps'/$PLL_CLUSTERS)
	local lsize = `csize' + (`reps' - `csize'*$PLL_CLUSTERS)

	/* Reserving a pll_id. This will be stored in the -parallelid- local
	macro */
	mata: parallel_sandbox(5)

	/* Saving the tmpfile */
	local tmpdta = "__pll`parallelid'_bs_dta.dta"
	if (`"`saving'"' == "") {
		if (c(os)=="Windows") local saving = `"`c(tmpdir)'__pll`parallelid'_bs_outdta.dta"'
		else local saving = `"`c(tmpdir)'/__pll`parallelid'_bs_outdta.dta"'
		local save = 0
	}
	else local save = 1
	local simul = `"__pll`parallelid'_bs_simul.do"'

	qui save `tmpdta'

	/* Parsing saving */
	_prefix_saving `saving'
	local saving    `"`s(filename)'"'
	if "`double'" == "" {
			local double    `"`s(double)'"'
	}
	local every     `"`s(every)'"'
	local replace   `"`s(replace)'"'

	cap confirm file `saving'
	if (!_rc & "`replace'" == "") {
		di "{error:File -`saving'- already exists, use the -replace- option}"
		if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'
		exit 602
	}

	/* Parsing a program */
	if (regexm(`"`model'"', "^([a-zA-Z0-9_]+)")) local cmd = regexs(1)
	cap findfile `cmd'.ado
	if (_rc) local programs `programs' `cmd'

	/* Creating a tmp program */
	tempname fh
	cap file open `fh' using `"`simul'"', w replace
	file write `fh' `"use `tmpdta', clear"' _n
	file write `fh' "if (\`pll_instance'==\$PLL_CLUSTERS) local reps = `lsize'" _n
	file write `fh' "else local reps = `csize'" _n
	file write `fh' `"local pll_instance : di %04.0f \`pll_instance'"' _n
	file write `fh' `"bs `expression', sav(__pll\`pll_id'_bs_eststore\`pll_instance', replace `double' `every') `options' rep(\`reps'): `model' `argopt'"' _n
	file close `fh'

	/* Running parallel */
	cap noi parallel do `simul', nodata programs(`programs') `mata' `noglobals' `seeds' ///
		`randtype' timeout(`timeout') processors(`processors') setparallelid(`parallelid') `keep' `keeplast'
	local pllseeds = r(pll_seeds)

	if (_rc) {
		if ("`keep'"=="" & "`keeplast'"=="") qui parallel clean, e(${LAST_PLL_ID}) force nologs
		mata: parallel_sandbox(2, "`parallelid'")
		if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'
		exit _rc
	}

	preserve

	/* Appending datasets */
	forval i=1/$PLL_CLUSTERS {
		quietly {
			local pll_instance : di %04.0f `i'
			use `"__pll$LAST_PLL_ID`'_bs_eststore`pll_instance'"', clear

			if ((`=`i'-1')) append using `saving'
			save `saving', replace
		}
	}

	/* Storing macros */
	local macros : r(macros)
	local scalars : r(scalars)
	foreach m of local macros {
		local `m' = r(`m')
	}
	foreach s of local scalars {
		local `s' = r(`s')
	}

	restore

	/* Returning bs data */
	bstat using `saving', title(parallel bootstrapping)

	/* Cleaning up */
	if ("`keep'"=="" & "`keeplast'"=="") parallel clean, e(${LAST_PLL_ID}) nologs
	mata: parallel_sandbox(2, "`parallelid'")

	parallel_bs_ereturn

	/* Getting macros back */
	if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'
	foreach m of local macros {
		return local `m'  `"``m''"'
	}
	foreach s of local scalars {
		return scalar `s' = ``s''
	}
	return local pll_seeds = "`pllseeds'"

end

program def parallel_bs_ereturn, eclass
	vers 11.0
	ereturn local pll 1
end


Added a little bit more

Added a little bit more

*! version 5.1 31MAY2017  Kristoffer Bjarkefur kbjarkefur@worldbank.org

cap	program drop	iegraph
	program define 	iegraph , rclass

	syntax varlist, [noconfbars BASICTItle(string) save(string) confbarsnone(varlist) 	///
						confintval(numlist min=1 max=1 >0 <1) VARLabels BAROPTions(string) norestore  	///
						GREYscale yzero *]

	if "`restore'" == "" preserve

	qui {

	version 11

	*Load regression results
	mat beta_ = e(b)

	local counter = 0

	*Checking to see if the noconfbars option has been used and assigning 1 and 0 based
	*on that to the CONFINT_BAR variable.
	if "`confbars'" 		!= "" {
		local CONFINT_BAR 	= 0
	}
	else if "`confbars'" 	== "" {
		local CONFINT_BAR 	= 1
	}

	*Testing to see if the variables used in confbarsnone are
	*actually in the list of
	*variables used for the regression/graph.
	local varTest : list confbarsnone in varlist

	if `varTest' == 0 {
		*Error displayed if variables defined in confbarsnone aren't in the list of variables for the graph.
		noi display as error "{phang} Variables defined in confbarsnone(`confbarsnone') cannot be found in the graph variable list. {p_end}"
		noi display ""
		error 111
	}

	*Testing to see if the variables used in the regressions are actual dummy variables as treatment vars need to be dummy variables.
	foreach var of local varlist{
		cap assert inlist(`var',0,1) | missing(`var')
		if _rc {
			noi display as error "{phang} The variable `var' is not a dummy. Treatment variable needs to be a dummy (0 or 1) variable. {p_end}"
			noi display ""
			error 149
		}

		local counter = `counter' + 1

		*Assigning variable coefficient/standard errors/no of obs. to scalars with the name Coeff_(`variable name')
		*coeff_se_(`variable name'), obs_(`variable name').

		scalar coeff_`var' 		= _b[`var']
		scalar coeff_se_`var' 	= _se[`var']

		count if e(sample) == 1 & `var' == 1 //Count one tmt group at the time
		scalar obs_`var' = r(N)

	}

	*Checking to see if the save option is used what is the extension related to it.
	if "`save'" != "" {

		**Find the last . in the path name and assume that
		* the file extension is what follows. However, with names
		* that have multiple dots in it, the user has to explicitly
		* specify the file name.

		**First, will extract the file names from the combination of file
		* path and files names.

		**Using both backslash and forward slash to account for Windows/Mac
		* paths
		local backslash = strpos(reverse("`save'"), "\")
		local forwardslash = strpos(reverse("`save'"), "/")

		if `forwardslash' == 0 | `backslash' == 0 {
			local file_name = substr(reverse("`save'"), 1, (max(`forwardslash', `backslash')-1))
		}
		else {
			local file_name = substr(reverse("`save'"), 1, (min(`forwardslash', `backslash')-1))
			}

		local file_name = reverse("`file_name'")

		*Assign the full file path to the local file_suffix
		local file_suffix = "`save'"

        *Find index for where the file type suffix start
        local dot_index     = strpos("`save'",".")

		*If no dot in the name, then no file extension
		if `dot_index' == 0 {
			local save `"`save'.gph"'
			local file_suffix ".gph"
			local save_export = 0
		}

		**If there is one or many . in the file path than loop over
		* the file path until we have found the last one.
		while `dot_index' > 0 {

			*Extract the file index
			local file_suffix 	= substr("`file_suffix'", `dot_index' + 1, .)

			*Find index for where the file type suffix start
			local dot_index 	= strpos("`file_suffix'",".")
		}

        *List of formats to which the file can be exported
        local nonGPH_formats png tiff gph ps eps pdf wmf emf

        *If no file format suffix is specified, use the default .gph
        if "`file_suffix'" == "gph" {
			local save_export = 0
		}

        *If a file format suffix is specified make sure that it is one of the eight formats allowed.
        else if `:list file_suffix in nonGPH_formats' != 0  {
            local save_export = 1

			if ("`file_suffix'" == "wmf" | "`file_suffix'" == "emf") & "`c(os)'" != "Windows" {
				di as error "{pstd}The file formats .wmf and .emf are only allowed when using Stata on a Windows computer.{p_end}"
                error 198
			}
		}
		*If a different extension was used then displaying an error.
        else {

            di as error "{pstd}You are not using a allowed file format in save(`save'). Only the following formats are allowed: gph `nonGPH_formats'. If you file ///
							or path name has a dot(.) in it, please specify full file extension. {p_end}"
            error 198
        }
    }
	else {

		*Save option is not used, therefore save export will not be used
		local save_export = 0

	}

	local count: word count `varlist' // Counting the number of total vars used as treatment.
	local graphCount = `count' + 1 // Number of vars needed for the graph is total treatment vars plus one(control).

	//Make all vars tempvars (maybe do later)
	//Make sure that missing is properly handled

	tempvar anyTMT control
	egen `anyTMT' = rowmax(`varlist') 	if e(sample) == 1
	gen `control' = (`anyTMT' == 0) 	if !missing(`anyTMT')

	sum `e(depvar)' if e(sample) == 1 & `control' == 1
	scalar ctl_N		= r(N)
	scalar ctl_mean	  	= r(mean)
	scalar ctl_mean_sd 	= r(sd)

	/**
	 Calculate t-statistics
	**/

	*If not set in options, use default of 95%
	if "`confintval'" == "" {
		local confintval = .95
	}

	**Since we calculating each tail separetely we need to convert
	* the two tail % to one tail %
	local conintval_1tail = ( `confintval' + (1-`confintval' ) / 2)

	*degreeds of freedom in regression
	local df = `e(df_r)'

	*Calculate t-stats to be used
	local tstats = invt(`df' , `conintval_1tail'  )

	foreach var of local varlist {

		*Caculating confidnece interval
		scalar  conf_int_min_`var'   =	coeff_`var'-(`tstats'*coeff_se_`var') + ctl_mean
		scalar  conf_int_max_`var'   =	coeff_`var'+(`tstats'*coeff_se_`var') + ctl_mean

		**Assigning stars to the treatment vars.

		*Perform the test to get p-values
		test `var'
		local star_`var' " "

		scalar  pvalue =r(p)
		if pvalue < 0.10 {
			local star_`var' "*"
		}
		if pvalue < 0.05 {
			local star_`var' "**"
		}
		if pvalue < 0.01 {
			local star_`var' "***"
		}

		scalar tmt_mean_`var' = ctl_mean + coeff_`var'
	}

	/*************************************

		Set up temp file where results are written

	*************************************/

	tempfile		 newTextFile
	tempname 		 newHandle
	cap file close 	`newHandle'
	file open  		`newHandle' using "`newTextFile'", text write append

	*Write headers and control value
	file write `newHandle' ///
		"position" 	_tab "xLabeled"	_tab "mean" _tab "coeff" _tab "conf_int_min" _tab "conf_int_max" _tab "obs" _tab "star" _n 	///
		%9.3f (1) _tab "Control"  _tab %9.3f (ctl_mean)      _tab  	_tab   _tab _tab %9.3f (ctl_N) 	 _tab  _n

	tempvar newCounter
	gen `newCounter' = 2  //First tmt group starts at 2 (1 is control)

	foreach var in `varlist' {

		if "`varlabels'" == "" {

			*Default is to use the varname in legend
			local var_legend "`var'"

		}
		else {

			*Option to use the variable label in the legen instead
			local var_legend : variable label `var'

		}

		*Writing the necessary tables for the graph to list to file.
	    if `: list var in confbarsnone' {
			file write `newHandle' 	%9.3f (`newCounter') _tab `"`var_legend'"'  ///
			_tab %9.3f (tmt_mean_`var') _tab  %9.3f (coeff_`var')  _tab _tab ///
			_tab %9.3f (obs_`var') _tab "`star_`var''"  _n

			replace `newCounter' = `newCounter' + 1
		}
		else {
			file write `newHandle' %9.3f (`newCounter') _tab `"`var_legend'"' ///
			_tab %9.3f (tmt_mean_`var') _tab  %9.3f (coeff_`var')  			///
			_tab %9.3f (conf_int_min_`var') _tab %9.3f (conf_int_max_`var')	///
			_tab %9.3f (obs_`var')   _tab "`star_`var''"  _n

			replace `newCounter' = `newCounter' + 1
		}
	}

	file close `newHandle'

	/*************************************

		Create the graph

	*************************************/

	*Rread file with results
	insheet using `newTextFile', clear

	*Defining various options to go on the graph option.

	local tmtGroupBars 	""
	local xAxisLabels 	`"xlabel( "'
	local legendLabels	""
	local legendNumbers	""

	forval tmtGroupCount = 1/`graphCount' {

		************
		*Create the bar for this group

		if "`greyscale'" == "" {
			colorPicker `tmtGroupCount' `graphCount'
		}
		else {
			greyPicker `tmtGroupCount' `graphCount'
		}

		local tmtGroupBars `"`tmtGroupBars' (bar mean position if position == `tmtGroupCount', `baroptions' color("`r(color)'") lcolor(black) ) "'

		************
		*Create labels etc. for this group

		local obs 			= obs[`tmtGroupCount']
		local stars 		= star[`tmtGroupCount']
		local legendLabel 	= xlabeled[`tmtGroupCount']

		local xAxisLabels 	`"`xAxisLabels' `tmtGroupCount' "(N = `obs') `stars'" "'
		local legendLabels 	`"`legendLabels' lab(`tmtGroupCount' "`legendLabel'") "'
		local legendNumbers	`"`legendNumbers' `tmtGroupCount'"'
	}

	*Close or comple some strings
	local xAxisLabels `"`xAxisLabels' ,noticks labsize(medsmall)) "'
	local legendOption `"legend(order(`legendNumbers') `legendLabels')"'

	*Create the confidence interval bars

	if `CONFINT_BAR' == 0 {

		local confIntGraph = ""
	}
		else if `CONFINT_BAR' == 1 {
		local confIntGraph = `"(rcap conf_int_max conf_int_min position, lc(gs)) (scatter mean position,  msym(none)  mlabs(medium) mlabpos(10) mlabcolor(black))"'
	}

	local titleOption `" , xtitle("") ytitle("`e(depvar)'") "'

	if "`save'" != "" {
		local saveOption saving("`save'", replace)
	}

	*******************************************************************************
	*** Generating the graph axis labels for the y-zero option used..
	*******************************************************************************

	if "`yzero'" != "" {

		*Finding the max value that is needed in the Y-axis.
		gen maxvalue = max(mean , conf_int_max)
		sum maxvalue

		*From the max of mean values and conf_int_max values.
		local max = `r(max)'

		*Log10 of the Max value to find the order necessary.
		local logmax = log10(`max')
		local logmax = round(`logmax') - 1

		*Generating tenpower which is 1 order smaller than the max value,
		*so we can log to that.
		local tenpower = 10 ^ (`logmax')

		*Rounding up for max value.
		local up = `tenpower' * ceil(`max' / `tenpower')

		*Generating quarter value for y-axis markers.
		local quarter = (`up') / 4

		*Specifying the option itself.
		local yzero_option ylabel(0(`quarter')`up')
	}

	*******************************************************************************
	***Graph generation based on if the option save has a export or a save feature.
	*******************************************************************************

	*Store all the options in one local
	local commandline 		`" `tmtGroupBars' `confIntGraph' `titleOption'  `legendOption' `xAxisLabels' title("`basictitle'") `yzero_option' `options'  "'

	*Error message used in both save-option cases below.
	local graphErrorMessage `" Something went wrong while trying to generate the graph. Click {stata di r(cmd) :display graph options } to see what graph options iegraph used. This can help in locating the source of the error in the command. "'

	if `save_export' == 0 {

		*Generate a return local with the code that will be used to generate the graph
		return local cmd `"graph twoway `commandline' `saveOption'"'

		*Generate the graph
		cap graph twoway `commandline' `saveOption'

		*If error, provide error message and then run the code again allowing the program to crash
		if _rc {

			di as error "{pstd}`graphErrorMessage'{p_end}"
            graph twoway `commandline' `saveOption'
		}
	}
	else if `save_export' == 1 {

		*Generate a return local with the code that will be used to generate the graph
		return local cmd `"graph twoway `commandline'"'

		*Generate the graph
		cap graph twoway `commandline'

		*If error, provide error message and then run the code again allowing the program to crash
		if _rc {

			di as error "{pstd}`graphErrorMessage'{p_end}"
            graph twoway `commandline'
		}

		*Export graph to preferred option
		graph export "`save'", replace

	}

	if "`restore'" == "" restore
}

end
	*******************************************
	*******************************************
		******* To pick colours based *******
		******* on the number of vars *******
	*******************************************
	*******************************************
	cap	program drop	colorPicker
		program define 	colorPicker , rclass

		args groupCount totalNumGroups

		if `totalNumGroups' == 2 {

			if `groupCount' == 1 return local color "215 25 28"
			if `groupCount' == 2 return local color "43 123 182"
		}
		else if `totalNumGroups' == 3 {

			if `groupCount' == 1 return local color "215 25 28"
			if `groupCount' == 2 return local color "255 255 191"
			if `groupCount' == 3 return local color "43 123 182"
		}
		else if `totalNumGroups' == 4 {

			if `groupCount' == 1 return local color "215 25 28"
			if `groupCount' == 2 return local color "255 255 191"
			if `groupCount' == 3 return local color "171 217 233"
			if `groupCount' == 4 return local color "43 123 182"
		}
		else {

			*For five or more colors we repeat the same pattern

			local colourNum = mod(`groupCount', 5)
			if `colourNum' == 1 return local color "215 25 28"
			if `colourNum' == 2 return local color "253 174 93"
			if `colourNum' == 3 return local color "255 255 191"
			if `colourNum' == 4 return local color "171 217 233"
			if `colourNum' == 0 return local color "43 123 182"

		}

	end
	*******************************************
	*******************************************
		******* Greyscale Option *******
		******* Colour Picker    *******
	*******************************************
	*******************************************

	cap	program drop	greyPicker
		program define 	greyPicker , rclass

		args groupCount totalNumGroups

		if `groupCount' == 1 {

			return local color "black"
		}
		else if `groupCount' == 2 & `totalNumGroups' <= 3 {

			return local color "gs14"
		}
		else {

			local grayscale =  round( (`groupCount'-1) * (100 / (`totalNumGroups'-1) ))

			return local color "`grayscale' `grayscale' `grayscale' `grayscale'"
		}

	end

*! version 5.1 31MAY2017  Kristoffer Bjarkefur kbjarkefur@worldbank.org

cap	program drop	iegraph
	program define 	iegraph , rclass

	syntax varlist, [noconfbars BASICTItle(string) save(string) confbarsnone(varlist) 	///
						confintval(numlist min=1 max=1 >0 <1) VARLabels BAROPTions(string) norestore  	///
						GREYscale yzero *]

	if "`restore'" == "" preserve

	qui {

	version 11

	*Load regression results
	mat beta_ = e(b)

	local counter = 0

	*Checking to see if the noconfbars option has been used and assigning 1 and 0 based
	*on that to the CONFINT_BAR variable.
	if "`confbars'" 		!= "" {
		local CONFINT_BAR 	= 0
	}
	else if "`confbars'" 	== "" {
		local CONFINT_BAR 	= 1
	}

	*Testing to see if the variables used in confbarsnone are
	*actually in the list of
	*variables used for the regression/graph.
	local varTest : list confbarsnone in varlist

	if `varTest' == 0 {
		*Error displayed if variables defined in confbarsnone aren't in the list of variables for the graph.
		noi display as error "{phang} Variables defined in confbarsnone(`confbarsnone') cannot be found in the graph variable list. {p_end}"
		noi display ""
		error 111
	}

	*Testing to see if the variables used in the regressions are actual dummy variables as treatment vars need to be dummy variables.
	foreach var of local varlist{
		cap assert inlist(`var',0,1) | missing(`var')
		if _rc {
			noi display as error "{phang} The variable `var' is not a dummy. Treatment variable needs to be a dummy (0 or 1) variable. {p_end}"
			noi display ""
			error 149
		}

		local counter = `counter' + 1

		*Assigning variable coefficient/standard errors/no of obs. to scalars with the name Coeff_(`variable name')
		*coeff_se_(`variable name'), obs_(`variable name').

		scalar coeff_`var' 		= _b[`var']
		scalar coeff_se_`var' 	= _se[`var']

		count if e(sample) == 1 & `var' == 1 //Count one tmt group at the time
		scalar obs_`var' = r(N)

	}

	*Checking to see if the save option is used what is the extension related to it.
	if "`save'" != "" {

		**Find the last . in the path name and assume that
		* the file extension is what follows. However, with names
		* that have multiple dots in it, the user has to explicitly
		* specify the file name.

		**First, will extract the file names from the combination of file
		* path and files names.

		local backslash = strpos(reverse("`save'"), "\")
		local forwardslash = strpos(reverse("`save'"), "/")
		**local slashpos = min(`backslash', `forwardslash')

		if `forwardslash' == 0 | `backslash' == 0 {
			local file_name = substr(reverse("`save'"), 1, (max(`forwardslash', `backslash')-1))
		}
		else {
			local file_name = substr(reverse("`save'"), 1, (min(`forwardslash', `backslash')-1))
			}

		di "`file_name'"

		*Assign the full file path to the local file_suffix
		local file_suffix = "`save'"

        *Find index for where the file type suffix start
        local dot_index     = strpos("`save'",".")

		*If no dot in the name, then no file extension
		if `dot_index' == 0 {
			local save `"`save'.gph"'
			local file_suffix ".gph"
			local save_export = 0
		}

		**If there is one or many . in the file path than loop over
		* the file path until we have found the last one.
		while `dot_index' > 0 {

			*Extract the file index
			local file_suffix 	= substr("`file_suffix'", `dot_index' + 1, .)

			*Find index for where the file type suffix start
			local dot_index 	= strpos("`file_suffix'",".")
		}

        *List of formats to which the file can be exported
        local nonGPH_formats png tiff gph ps eps pdf wmf emf

        *If no file format suffix is specified, use the default .gph
        if "`file_suffix'" == "gph" {
			local save_export = 0
		}

        *If a file format suffix is specified make sure that it is one of the eight formats allowed.
        else if `:list file_suffix in nonGPH_formats' != 0  {
            local save_export = 1

			if ("`file_suffix'" == "wmf" | "`file_suffix'" == "emf") & "`c(os)'" != "Windows" {
				di as error "{pstd}The file formats .wmf and .emf are only allowed when using Stata on a Windows computer.{p_end}"
                error 198
			}
		}
		*If a different extension was used then displaying an error.
        else {

            di as error "{pstd}You are not using a allowed file format in save(`save'). Only the following formats are allowed: gph `nonGPH_formats'. If you file ///
							or path name has a dot(.) in it, please specify full file extension. {p_end}"
            error 198
        }
    }
	else {

		*Save option is not used, therefore save export will not be used
		local save_export = 0

	}

	local count: word count `varlist' // Counting the number of total vars used as treatment.
	local graphCount = `count' + 1 // Number of vars needed for the graph is total treatment vars plus one(control).

	//Make all vars tempvars (maybe do later)
	//Make sure that missing is properly handled

	tempvar anyTMT control
	egen `anyTMT' = rowmax(`varlist') 	if e(sample) == 1
	gen `control' = (`anyTMT' == 0) 	if !missing(`anyTMT')

	sum `e(depvar)' if e(sample) == 1 & `control' == 1
	scalar ctl_N		= r(N)
	scalar ctl_mean	  	= r(mean)
	scalar ctl_mean_sd 	= r(sd)

	/**
	 Calculate t-statistics
	**/

	*If not set in options, use default of 95%
	if "`confintval'" == "" {
		local confintval = .95
	}

	**Since we calculating each tail separetely we need to convert
	* the two tail % to one tail %
	local conintval_1tail = ( `confintval' + (1-`confintval' ) / 2)

	*degreeds of freedom in regression
	local df = `e(df_r)'

	*Calculate t-stats to be used
	local tstats = invt(`df' , `conintval_1tail'  )

	foreach var of local varlist {

		*Caculating confidnece interval
		scalar  conf_int_min_`var'   =	coeff_`var'-(`tstats'*coeff_se_`var') + ctl_mean
		scalar  conf_int_max_`var'   =	coeff_`var'+(`tstats'*coeff_se_`var') + ctl_mean

		**Assigning stars to the treatment vars.

		*Perform the test to get p-values
		test `var'
		local star_`var' " "

		scalar  pvalue =r(p)
		if pvalue < 0.10 {
			local star_`var' "*"
		}
		if pvalue < 0.05 {
			local star_`var' "**"
		}
		if pvalue < 0.01 {
			local star_`var' "***"
		}

		scalar tmt_mean_`var' = ctl_mean + coeff_`var'
	}

	/*************************************

		Set up temp file where results are written

	*************************************/

	tempfile		 newTextFile
	tempname 		 newHandle
	cap file close 	`newHandle'
	file open  		`newHandle' using "`newTextFile'", text write append

	*Write headers and control value
	file write `newHandle' ///
		"position" 	_tab "xLabeled"	_tab "mean" _tab "coeff" _tab "conf_int_min" _tab "conf_int_max" _tab "obs" _tab "star" _n 	///
		%9.3f (1) _tab "Control"  _tab %9.3f (ctl_mean)      _tab  	_tab   _tab _tab %9.3f (ctl_N) 	 _tab  _n

	tempvar newCounter
	gen `newCounter' = 2  //First tmt group starts at 2 (1 is control)

	foreach var in `varlist' {

		if "`varlabels'" == "" {

			*Default is to use the varname in legend
			local var_legend "`var'"

		}
		else {

			*Option to use the variable label in the legen instead
			local var_legend : variable label `var'

		}

		*Writing the necessary tables for the graph to list to file.
	    if `: list var in confbarsnone' {
			file write `newHandle' 	%9.3f (`newCounter') _tab `"`var_legend'"'  ///
			_tab %9.3f (tmt_mean_`var') _tab  %9.3f (coeff_`var')  _tab _tab ///
			_tab %9.3f (obs_`var') _tab "`star_`var''"  _n

			replace `newCounter' = `newCounter' + 1
		}
		else {
			file write `newHandle' %9.3f (`newCounter') _tab `"`var_legend'"' ///
			_tab %9.3f (tmt_mean_`var') _tab  %9.3f (coeff_`var')  			///
			_tab %9.3f (conf_int_min_`var') _tab %9.3f (conf_int_max_`var')	///
			_tab %9.3f (obs_`var')   _tab "`star_`var''"  _n

			replace `newCounter' = `newCounter' + 1
		}
	}

	file close `newHandle'

	/*************************************

		Create the graph

	*************************************/

	*Rread file with results
	insheet using `newTextFile', clear

	*Defining various options to go on the graph option.

	local tmtGroupBars 	""
	local xAxisLabels 	`"xlabel( "'
	local legendLabels	""
	local legendNumbers	""

	forval tmtGroupCount = 1/`graphCount' {

		************
		*Create the bar for this group

		if "`greyscale'" == "" {
			colorPicker `tmtGroupCount' `graphCount'
		}
		else {
			greyPicker `tmtGroupCount' `graphCount'
		}

		local tmtGroupBars `"`tmtGroupBars' (bar mean position if position == `tmtGroupCount', `baroptions' color("`r(color)'") lcolor(black) ) "'

		************
		*Create labels etc. for this group

		local obs 			= obs[`tmtGroupCount']
		local stars 		= star[`tmtGroupCount']
		local legendLabel 	= xlabeled[`tmtGroupCount']

		local xAxisLabels 	`"`xAxisLabels' `tmtGroupCount' "(N = `obs') `stars'" "'
		local legendLabels 	`"`legendLabels' lab(`tmtGroupCount' "`legendLabel'") "'
		local legendNumbers	`"`legendNumbers' `tmtGroupCount'"'
	}

	*Close or comple some strings
	local xAxisLabels `"`xAxisLabels' ,noticks labsize(medsmall)) "'
	local legendOption `"legend(order(`legendNumbers') `legendLabels')"'

	*Create the confidence interval bars

	if `CONFINT_BAR' == 0 {

		local confIntGraph = ""
	}
		else if `CONFINT_BAR' == 1 {
		local confIntGraph = `"(rcap conf_int_max conf_int_min position, lc(gs)) (scatter mean position,  msym(none)  mlabs(medium) mlabpos(10) mlabcolor(black))"'
	}

	local titleOption `" , xtitle("") ytitle("`e(depvar)'") "'

	if "`save'" != "" {
		local saveOption saving("`save'", replace)
	}

	*******************************************************************************
	*** Generating the graph axis labels for the y-zero option used..
	*******************************************************************************

	if "`yzero'" != "" {

		*Finding the max value that is needed in the Y-axis.
		gen maxvalue = max(mean , conf_int_max)
		sum maxvalue

		*From the max of mean values and conf_int_max values.
		local max = `r(max)'

		*Log10 of the Max value to find the order necessary.
		local logmax = log10(`max')
		local logmax = round(`logmax') - 1

		*Generating tenpower which is 1 order smaller than the max value,
		*so we can log to that.
		local tenpower = 10 ^ (`logmax')

		*Rounding up for max value.
		local up = `tenpower' * ceil(`max' / `tenpower')

		*Generating quarter value for y-axis markers.
		local quarter = (`up') / 4

		*Specifying the option itself.
		local yzero_option ylabel(0(`quarter')`up')
	}

	*******************************************************************************
	***Graph generation based on if the option save has a export or a save feature.
	*******************************************************************************

	*Store all the options in one local
	local commandline 		`" `tmtGroupBars' `confIntGraph' `titleOption'  `legendOption' `xAxisLabels' title("`basictitle'") `yzero_option' `options'  "'

	*Error message used in both save-option cases below.
	local graphErrorMessage `" Something went wrong while trying to generate the graph. Click {stata di r(cmd) :display graph options } to see what graph options iegraph used. This can help in locating the source of the error in the command. "'

	if `save_export' == 0 {

		*Generate a return local with the code that will be used to generate the graph
		return local cmd `"graph twoway `commandline' `saveOption'"'

		*Generate the graph
		cap graph twoway `commandline' `saveOption'

		*If error, provide error message and then run the code again allowing the program to crash
		if _rc {

			di as error "{pstd}`graphErrorMessage'{p_end}"
            graph twoway `commandline' `saveOption'
		}
	}
	else if `save_export' == 1 {

		*Generate a return local with the code that will be used to generate the graph
		return local cmd `"graph twoway `commandline'"'

		*Generate the graph
		cap graph twoway `commandline'

		*If error, provide error message and then run the code again allowing the program to crash
		if _rc {

			di as error "{pstd}`graphErrorMessage'{p_end}"
            graph twoway `commandline'
		}

		*Export graph to preferred option
		graph export "`save'", replace

	}

	if "`restore'" == "" restore
}

end
	*******************************************
	*******************************************
		******* To pick colours based *******
		******* on the number of vars *******
	*******************************************
	*******************************************
	cap	program drop	colorPicker
		program define 	colorPicker , rclass

		args groupCount totalNumGroups

		if `totalNumGroups' == 2 {

			if `groupCount' == 1 return local color "215 25 28"
			if `groupCount' == 2 return local color "43 123 182"
		}
		else if `totalNumGroups' == 3 {

			if `groupCount' == 1 return local color "215 25 28"
			if `groupCount' == 2 return local color "255 255 191"
			if `groupCount' == 3 return local color "43 123 182"
		}
		else if `totalNumGroups' == 4 {

			if `groupCount' == 1 return local color "215 25 28"
			if `groupCount' == 2 return local color "255 255 191"
			if `groupCount' == 3 return local color "171 217 233"
			if `groupCount' == 4 return local color "43 123 182"
		}
		else {

			*For five or more colors we repeat the same pattern

			local colourNum = mod(`groupCount', 5)
			if `colourNum' == 1 return local color "215 25 28"
			if `colourNum' == 2 return local color "253 174 93"
			if `colourNum' == 3 return local color "255 255 191"
			if `colourNum' == 4 return local color "171 217 233"
			if `colourNum' == 0 return local color "43 123 182"

		}

	end
	*******************************************
	*******************************************
		******* Greyscale Option *******
		******* Colour Picker    *******
	*******************************************
	*******************************************

	cap	program drop	greyPicker
		program define 	greyPicker , rclass

		args groupCount totalNumGroups

		if `groupCount' == 1 {

			return local color "black"
		}
		else if `groupCount' == 2 & `totalNumGroups' <= 3 {

			return local color "gs14"
		}
		else {

			local grayscale =  round( (`groupCount'-1) * (100 / (`totalNumGroups'-1) ))

			return local color "`grayscale' `grayscale' `grayscale' `grayscale'"
		}

	end