iefolder sections and comments

iefolder sections and comments

cap program drop 	iefolder
	program define	iefolder

	syntax anything, projectfolder(string) [abb(string)]

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

		Parse input

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

	gettoken subcommand item : anything

	gettoken itemType itemName : item

	local subcomand = trim("`subcommand'")
	local itemType 	= trim("`itemType'")
	local itemName 	= trim("`itemName'")

	di "SubCommand 	`subcommand'"
	di "ItemType 	`itemType'"
	di "ItemName	`itemName'"

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

		Test input

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

	local sub_commands "new rename"
	local itemTypes "project round master admin monitor "

	*Test if subcommand is valid
	if `:list subcommand in sub_commands' == 0 {

		di as error "{phang}You have not used a valid subcommand. See help file for details.{p_end}"
		error 198
	}

	*Test if item type is valid
	if `:list itemType in itemTypes' == 0 {

		di as error "{phang}You have not used a valid item type. See help file for details.{p_end}"
		error 198
	}

	*Test that item name is used when item type is anything but project
	else if ("`itemType'" != "project" & "`itemName'" == "" ) {

		di as error "{phang}You must specify a name of the `itemType'. See help file for details.{p_end}"
		error 198

	}

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

		Start making updates to the project folder

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

	*Create a temporary textfile
	tempname 	newHandle
	tempfile	newTextFile

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

	if "`sub_commands'" == "new" {
		*Creating a new project
		if "`itemType'" == "project" {

			di "THIS: Project"
			iefolder_newProject "`projectfolder'" `newHandle'
		}
		*Creating a new round
		else if "`itemType'" == "round" {

			*Use the full item name if abbrevaition was not specified
			if "`abb'" == "" local abb "`itemName'"

			di "THIS: round"
			iefolder_newRound `newHandle' "`itemName'" "`abb'"
		}
		*Creating a new master data set
		else if "`itemType'" == "master" {

			di "THIS: master"
			iefolder_newMaster
		}
		*Creating a new admin
		else if "`itemType'" == "admin" {

			di "THIS: admin"
			iefolder_newAdmin
		}
		*Creating a new monitor
		else if "`itemType'" == "monitor" {

			di "THIS: monitor"
			iefolder_newMonitor
		}
	}

	*Closing the new main master dofile handle
	file close 		`newHandle'

	*Copy the new master dofile from the tempfile to the original position
	copy "`newTextFile'"  "$projectfolder/testMasterDofile.do" , replace

end

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

		Here are all functions for combination of
		subcommand and item type

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

cap program drop 	iefolder_newProject
	program define	iefolder_newProject

		args projectfolder newHandle

		mkdir "`projectfolder'/DataWork"

		global projectfolder "`projectfolder'/DataWork"

		******************************
		*Writing master do file header
		******************************

		*Write intro part with description of project
		masterDofilePart0 `newHandle'

		*Write devisor starting standardize section
		writeDevisor  `newHandle' 0 Standardize

		*Write section with ssc install and ieboilstart
		masterDofilePart0a `newHandle'

		*Write devisor ending standardize section
		writeDevisor `newHandle' 0 End_Standardize

		*Write devisor starting globals section
		writeDevisor  `newHandle' 1 Globals

		*Write globals section header and the root folders
		masterDofilePart1 `newHandle' "$projectfolder"

		*Write sub devisor starting master and monitor data section section
		writeDevisor `newHandle' 1 Globals 1 MastMon

		*Create master data folder and add global to folder in master do file
		createFolderWriteGlobal "Master Data"  "projectfolder"  mastData  `newHandle'

		*Create monitor data folder and add global to folder in master do file
		createFolderWriteGlobal "Monitor Data" "projectfolder"  moniData  `newHandle'

		*Write devisor ending globals section
		writeDevisor `newHandle' 1 End_Globals

end

cap program drop 	iefolder_newRound
	program define	iefolder_newRound

	args subHandle rndName rndAbb

	*Old file reference
	tempname 	oldHandle
	local 		oldTextFile 	"$projectfolder/testMasterDofile.do"

	file open `oldHandle' using `"`oldTextFile'"', read
	file read `oldHandle' line

	*Locals needed for the section devider
	local sectionNum 	= 0 //Keeps track of the section number
	local partNum 		= 0 //Keeps track of the part number

	while r(eof)==0 {

		di `"`line'"'

		*Do not interpret macros
		local line : subinstr local line "\$"  "\\$"
		local line : subinstr local line "\`"  "\\`"

		parseReadLine `"`line'"'

		if `r(ief_line)' == 0 {

			*This is a regular line of code. It should be copied as-is. No action needed.
			file write	`subHandle' `"`line'"' _n

		}
		else if `r(ief_line)' == 1 {

			**This is a line of code with a devisor written by
			* iefodler. New additions to a section are always added right before an end of section line.

			return list

			**This is NOT an end of section line. Nothing will be written here
			* but we test that there is no confict with previous names
			if `r(ief_end)' == 0 {

				*Test that the folder name about to be used is not already in use
				if "`r(sectionName)'" == "`rndName'" {
					di as error "{phang}The new round name `rndName' is already in use. No new folders are creaetd and the master do-files has not been changed.{p_end}"
					error 507
				}

				*Test that the folder name about to be used is not already in use
				if "`r(sectionAbb)'" == "`rndAbb'" {
					di as error "{phang}The new round abbreviation `rndAbb' is already in use. No new folders are creaetd and the master do-files has not been changed.{p_end}"
					error 507
				}

				*Copy the line as is
				file write		`subHandle' `"`line'"' _n

				*Update the section counter
				if "`r(sectionNum)'" != "*" {
					local sectionNum = `r(sectionNum)'
				}
			}

			**This is an end of section line. We will add the new content here
			* before writing the end of section line
			else if `r(ief_end)' == 1 {

				*Test if it is an end of globals section as we are writing a new
				if "`r(partName)'" == "End_Globals" {

					*Increment the section number for the new section
					local ++sectionNum

					*Write devisor for this section
					writeDevisor 			`subHandle' 1 Globals `sectionNum' `rndName' `rndAbb'

					*Write the globals to the master do file and create the folders
					newRndFolderAndGlobals 	`rndName' `rndAbb' "projectfolder" `subHandle'

					*Write an empty line before the end devisor
					file write		`subHandle' "" _n

				}

				*Write the end of section line
				file write		`subHandle' `"`line'"' _n
			}

		}

		*Read next file and repeat the while loop
		file read `oldHandle' line

		di "new line read"

	}

	*Close the old file.
	file close `oldHandle'

end

cap program drop 	iefolder
	program define	iefolder

	syntax anything, projectfolder(string) [abb(string)]

	gettoken subcommand item : anything

	gettoken itemType itemName : item

	local subcomand = trim("`subcommand'")
	local itemType 	= trim("`itemType'")
	local itemName 	= trim("`itemName'")

	di "SubCommand 	`subcommand'"
	di "ItemType 	`itemType'"
	di "ItemName	`itemName'"

	local sub_commands "new rename"
	local itemTypes "project round master admin monitor "

	*Test if subcommand is valid
	if `:list subcommand in sub_commands' == 0 {

		di as error "{phang}You have not used a valid subcommand. See help file for details.{p_end}"
		error 198
	}

	*Test if item type is valid
	if `:list itemType in itemTypes' == 0 {

		di as error "{phang}You have not used a valid item type. See help file for details.{p_end}"
		error 198
	}

	*Test that item name is used when item type is anything but project
	else if ("`itemType'" != "project" & "`itemName'" == "" ) {

		di as error "{phang}You must specify a name of the `itemType'. See help file for details.{p_end}"
		error 198

	}

	*Create a temporary textfile
	tempname 	newHandle
	tempfile	newTextFile

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

	*Creating a new project
	if "`itemType'" == "project" {

		di "THIS: Project"
		iefolder_newProject "`projectfolder'" `newHandle'
	}

	*Creating a new round
	else if "`itemType'" == "round" {

		*Use the full item name if abbrevaition was not specified
		if "`abb'" == "" local abb "`itemName'"

		di "THIS: round"
		iefolder_newRound `newHandle' "`itemName'" "`abb'"

	}

	*Creating a new master data set
	else if "`itemType'" == "master" {

		di "THIS: round"
		iefolder_newMaster

	}

	*Creating a new admin
	else if "`itemType'" == "admin" {

		di "THIS: round"
		iefolder_newAdmin

	}

	*Creating a new monitor
	else if "`itemType'" == "monitor" {

		di "THIS: round"
		iefolder_newMonitor

	}

	else {
		di "THIS: No item"
	}

	*Closing the new main master dofile handle
	file close 		`newHandle'

	*Copy the new master dofile from the tempfile to the original position
	copy "`newTextFile'"  "$projectfolder/testMasterDofile.do" , replace

end

/*

	The functions below deal with each seperate combination of subcommand and item type

*/

cap program drop 	iefolder_newProject
	program define	iefolder_newProject

		args projectfolder newHandle

		mkdir "`projectfolder'/DataWork"

		global projectfolder "`projectfolder'/DataWork"

		******************************
		*Writing master do file header
		******************************

		*Write intro part with description of project
		masterDofilePart0 `newHandle'

		*Write devisor starting standardize section
		writeDevisor  `newHandle' 0 Standardize

		*Write section with ssc install and ieboilstart
		masterDofilePart0a `newHandle'

		*Write devisor ending standardize section
		writeDevisor `newHandle' 0 End_Standardize

		*Write devisor starting globals section
		writeDevisor  `newHandle' 1 Globals

		*Write globals section header and the root folders
		masterDofilePart1 `newHandle' "$projectfolder"

		*Write sub devisor starting master and monitor data section section
		writeDevisor `newHandle' 1 Globals 1 MastMon

		*Create master data folder and add global to folder in master do file
		createFolderWriteGlobal "Master Data"  "projectfolder"  mastData  `newHandle'

		*Create monitor data folder and add global to folder in master do file
		createFolderWriteGlobal "Monitor Data" "projectfolder"  moniData  `newHandle'

		*Write devisor ending globals section
		writeDevisor `newHandle' 1 End_Globals

end

cap program drop 	iefolder_newRound
	program define	iefolder_newRound

	args subHandle rndName rndAbb

	*Old file reference
	tempname 	oldHandle
	local 		oldTextFile 	"$projectfolder/testMasterDofile.do"

	file open `oldHandle' using `"`oldTextFile'"', read
	file read `oldHandle' line

	*Locals needed for the section devider
	local sectionNum 	= 0 //Keeps track of the section number
	local partNum 		= 0 //Keeps track of the part number

	while r(eof)==0 {

		di `"`line'"'

		*Do not interpret macros
		local line : subinstr local line "\$"  "\\$"
		local line : subinstr local line "\`"  "\\`"

		parseReadLine `"`line'"'

		if `r(ief_line)' == 0 {

			*This is a regular line of code. It should be copied as-is. No action needed.
			file write	`subHandle' `"`line'"' _n

		}
		else if `r(ief_line)' == 1 {

			**This is a line of code with a devisor written by
			* iefodler. New additions to a section are always added right before an end of section line.

			return list

			**This is NOT an end of section line. Nothing will be written here
			* but we test that there is no confict with previous names
			if `r(ief_end)' == 0 {

				*Test that the folder name about to be used is not already in use
				if "`r(sectionName)'" == "`rndName'" {
					di as error "{phang}The new round name `rndName' is already in use. No new folders are creaetd and the master do-files has not been changed.{p_end}"
					error 507
				}

				*Test that the folder name about to be used is not already in use
				if "`r(sectionAbb)'" == "`rndAbb'" {
					di as error "{phang}The new round abbreviation `rndAbb' is already in use. No new folders are creaetd and the master do-files has not been changed.{p_end}"
					error 507
				}

				*Copy the line as is
				file write		`subHandle' `"`line'"' _n

				*Update the section counter
				if "`r(sectionNum)'" != "*" {
					local sectionNum = `r(sectionNum)'
				}
			}

			**This is an end of section line. We will add the new content here
			* before writing the end of section line
			else if `r(ief_end)' == 1 {

				*Test if it is an end of globals section as we are writing a new
				if "`r(partName)'" == "End_Globals" {

					*Increment the section number for the new section
					local ++sectionNum

					*Write devisor for this section
					writeDevisor 			`subHandle' 1 Globals `sectionNum' `rndName' `rndAbb'

					*Write the globals to the master do file and create the folders
					newRndFolderAndGlobals 	`rndName' `rndAbb' "projectfolder" `subHandle'

					*Write an empty line before the end devisor
					file write		`subHandle' "" _n

				}

				*Write the end of section line
				file write		`subHandle' `"`line'"' _n
			}

		}

		*Read next file and repeat the while loop
		file read `oldHandle' line

		di "new line read"

	}

	*Close the old file.
	file close `oldHandle'

end


fix comment in redo.do

fix comment in redo.do

# Dependencies:
source=`ls *.hs`
redo-ifchange $source

PROFILE="-prof -fprof-auto -rtsopts"
#PROFILE=
ROOT=`pwd`/..
SANDBOX=$ROOT/.cabal-sandbox/x86_64-osx-ghc-7.8.3-packages.conf.d
PACKAGE="-package-db=$SANDBOX"

# Compile Main.hs to temporary filename $3
ghc -O2 -v1 -Wall $PACKAGE $PROFILE -o $3 Main.hs 1>&2

# Dependencies:
source=`ls *.hs`
redo-ifchange $source

PROFILE="-prof -fprof-auto -rtsopts"
#PROFILE=
ROOT=`pwd`/..
SANDBOX=$ROOT/.cabal-sandbox/x86_64-osx-ghc-7.8.3-packages.conf.d
PACKAGE="-package-db=$SANDBOX"

# Compile redo.hs to filename $3 with verbosity set to low
# so there is nothing printed to standard out
ghc -O2 -v1 -Wall $PACKAGE $PROFILE -o $3 Main.hs 1>&2


path to personal ado folder

path to personal ado folder

/*
  Create highchart plot from dataset
  Ben Southgate
  08/20/14
*/

capture program drop injectFile
program define injectFile
  /*
    Read a file (`1') and write it line by line
    to another file (`2'). Both files should be
    passed as file object handles.
  */
  file read `1' line
  while r(eof) == 0 {
    if ("`3'" == "1") {
      file write `2' `"`macval(line)'"' "\n"
    }
    else {
      file write `2' `"`macval(line)'"' _n //"
    }
    file read `1' line
  }
end

capture program drop stataHC
program define stataHC

  syntax varlist(min=2 numeric) [if] using/ [, ///
      title(string) type(string) xdate(integer 0) ///
      xTitle(string) yTitle(string) by(varname) replace ///
    ]

  // Grab passed variables
  tokenize `varlist'
  local first `1'
  macro shift
  local rest `*'

  // Create list of all secondary variables
  local y_var_string "["
  foreach v in `rest' {
    local c ","
    if ("`v'" == "`: word 1 of `rest''") {
      local c ""
    }
    local y_var_string "`y_var_string' `c' '`v''"
  }
  local y_var_string "`y_var_string']"

  // Create a temporary csv file and write out the current dataset
  tempfile tcsv

  // temporary names for files
  tempname thtml_file
  tempname tcsv_file
  tempname tjs_file

  // The start of the new html file
  local html_top ///
    ///
      ///
      <meta http-equiv='Content-Type' content='text/htmlcharset=utf-8' / > ///
        ///
        ///
     ///
     ///

 ///
      "

end

/*
  Create highchart plot from dataset
  Ben Southgate
  08/20/14
*/

// This needs to be the path to the folder containing this dofile and stataHC.js
global STATA_HC_PATH_TO_FOLDER "D:/Ado/personal"

capture program drop injectFile
program define injectFile
  /*
    Read a file (`1') and write it line by line
    to another file (`2'). Both files should be
    passed as file object handles.
  */
  file read `1' line
  while r(eof) == 0 {
    if ("`3'" == "1") {
      file write `2' `"`macval(line)'"' "\n"
    }
    else {
      file write `2' `"`macval(line)'"' _n //"
    }
    file read `1' line
  }
end

capture program drop stataHC
program define stataHC

  syntax varlist(min=2 numeric) [if] using/ [, ///
      title(string) type(string) xdate(integer 0) ///
      xTitle(string) yTitle(string) by(varname) replace ///
    ]

  // Grab passed variables
  tokenize `varlist'
  local first `1'
  macro shift
  local rest `*'

  // Create list of all secondary variables
  local y_var_string "["
  foreach v in `rest' {
    local c ","
    if ("`v'" == "`: word 1 of `rest''") {
      local c ""
    }
    local y_var_string "`y_var_string' `c' '`v''"
  }
  local y_var_string "`y_var_string']"

  // Create a temporary csv file and write out the current dataset
  tempfile tcsv

  // temporary names for files
  tempname thtml_file
  tempname tcsv_file
  tempname tjs_file

  // The start of the new html file
  local html_top ///
    ///
      ///
      <meta http-equiv='Content-Type' content='text/htmlcharset=utf-8' / > ///
        ///
        ///
     ///
     ///

 ///
      "

end


Fix runpython.do for systems with unxpected configurations.

If the expected arch directory doesn't exist, give up and don't specify arch at
all. Currently it expands to '*' which fails.

[slightly modified by apenwarr]

exec >&2
redo-ifchange runpython.c
ARCHES=""
printf "Platforms: "
if [ -d /usr/libexec/gcc/darwin ]; then
    for d in /usr/libexec/gcc/darwin/*; do
        PLAT=$(basename "$d")
        [ "$PLAT" != "ppc64" ] || continue  # fails for some reason on my Mac
        ARCHES="$ARCHES -arch $PLAT"
        printf "$PLAT "
    done
fi
printf "\n"
gcc $ARCHES \
	-Wall -o $3 runpython.c \
	-I/usr/include/python2.5 \
	-lpython2.5

exec >&2
redo-ifchange runpython.c
ARCHES=""
printf "Platforms: "
for d in /usr/libexec/gcc/darwin/*; do
    PLAT=$(basename "$d")
    [ "$PLAT" != "ppc64" ] || continue  # fails for some reason on my Mac
    ARCHES="$ARCHES -arch $PLAT"
    printf "$PLAT "
done
printf "\n"
gcc $ARCHES \
	-Wall -o $3 runpython.c \
	-I/usr/include/python2.5 \
	-lpython2.5


Switch away from vasm to use a.py

Switch away from vasm to use a.py

redo-ifchange ../src/a/*.py
redo-ifchange ../src/mlm/mlm.asm
../src/a/a.py from ../src/mlm/mlm.asm to $3 quiet

redo-ifchange ../src/vasm/*.f
redo-ifchange ../src/mlm/*.f ../src/mlm/*.asm ../src/mlm/*.i
sf ../src/mlm/mlm.f $3 >&2


Revert "ieboilstart : used test command name in final .ado file"

This reverts commit 5e5a93e10079aa376b66b4643f73ddc9e76529b4.

	*! version 2.0 24Oct2016  Kristoffer Bjarkefur kbjarkefur@worldbank.org

	capture program drop ieboilstart2
	program ieboilstart2 , rclass

	qui {

		syntax using ,  Versionnumber(string) [noclear maxvar(numlist) matsize(numlist) QUIetdo CUSTom(string) ]

		*Potentional updates
		*set trace on
		*set scrollbufsize #

		version 10.0
		version `versionnumber'

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

			Check file name

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

		*Parse out the file name
		tokenize `"`using'"'
		local 		textfile "`2'"

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

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

		*If no file format suffix is specified, use the default .xlsx
		if "`file_suffix'" == "" {

			local textfile `"`textfile'.do"'
		}

		*If a file format suffix is specified make sure that it is one of the two allowed.
		else if "`file_suffix'" != ".do" {

			noi display as error "{phang}The file format specified in using is other than .do. Only the .do format is allowed. If no format is specified .do is assumed.{p_end}"
			error 198
		}

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

			Check version

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

		if "`maxvar'" != "" & "`clear'" != "" {

			di as error "{phang}It is not possible to set allowed maximum numbers of variables without clearing the data. noclear and maxvar() can therefore not be specified at the same time{p_end}"
			di ""
			error 198
			exit
		}

		local stata_versions "10.0 10.1 11.0 11.1 11.2 12.0 12.1 13.0 13.1 14.0 14.1"

		if `:list versionnumber in stata_versions' == 0 {

			di as error "{phang}Only recent major releases are allowed. One decimal must always be included. The releases currently allowed are:{break}`stata_versions'{p_end}"
			di ""
			error 198
			exit
		}

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

			Check input for maxvar and matsize
			if specified, other wise set
			maximum value allowed.

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

		local stata_types ic se mp
		foreach maxlocal in maxvar matsize {

			if "`maxlocal'" == "maxvar" {
				if c(MP) == 1 | c(SE) == 1 {
					local max 32767
					local min 2048
				}
				else {
					local max 2047
					local min 2047
				}
			}

			if "`maxlocal'" == "matsize" {
				if c(MP) == 1 | c(SE) == 1 {
					local max 11000
					local min 10
				}
				else {
					local max 800
					local min 10
				}
			}

			if c(MP) == 1 | c(SE) == 1 {
				local vusing "Stata SE and Stata MP"
			}
			else {
				local vusing "Stata IC"
			}

			*Test if user set maxvar
			if "``maxlocal''" != "" {

				if ``maxlocal'' >= `min' & ``maxlocal'' <= `max' {

					local `maxlocal' ``maxlocal''
				}
				else {

					di as error "{phang}`maxlocal' must be between `min' and `max' (inclusive) if you are using `vusing'. You entered ``maxlocal''.{p_end}"
					if ``maxlocal'' < `min' {
						di ""
						error 910
					}
					else {
						di ""
						error 912
					}
					exit
				}
			}
			else {

				*User did not specify value, use max value allowed
				local `maxlocal' `max'
			}
		}

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

			Prepare a local with all settings

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

		local return_string ""

		*Set verison number
		local return_string `return_string' version `versionnumber' ;

		*Set basic memory limits
		if "`clear'" == "" {
			local return_string `return_string' clear all ;
			local return_string `return_string' set maxvar 	`maxvar' ;
		}

		local return_string `return_string'  set matsize 	`matsize' ;

		*Set advanced memory limits
		local return_string `return_string' set niceness	5 ;

		if "`clear'" == "" {
			*These settings cannot be modified with data in memory
			local return_string `return_string' set min_memory	0 ;
			local return_string `return_string' set max_memory	. ;

			if c(bit) == 64 {
				local return_string `return_string' set segmentsize	32m ;
			}
			else {
				local return_string `return_string' set segmentsize	16m ;
			}
		}

		*Set default options
		local return_string `return_string' set more 		off ;
		local return_string `return_string' pause 			on 	;
		local return_string `return_string' set varabbrev 	off ;

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

			Add custom lines of code

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

		if "`custom'" != "" {

			*Add a comment that list where
			local return_string `return_string' ** The lines of code below was added manually when iboilstart ;
			local return_string `return_string' *  was used to create  this do-file. If there are any issues ;
			local return_string `return_string' *  with the code below, please reconfigure ieboilstart and ;
			local return_string `return_string' *  generate a new do-file. ;

			*Create a local with the rowlabel input to be tokenized
			local custom_code_lines `custom'

			while "`custom_code_lines'" != "" {

				*Parsing name and label pair
				gettoken code_line custom_code_lines : custom_code_lines, parse("@")

				*Removing leadning or trailing spaces
				local code_line = trim("`code_line'")

				local return_string `return_string' `code_line';

				*Parse char is not removed by gettoken
				local custom_code_lines = subinstr("`custom_code_lines'" ,"@","",1)
			}
		}

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

			Create the do-file

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

		*Create a local with the date
		local		date = subinstr(c(current_date)," ","",.)

		*Write the title rows defined above
		capture file close settingsfile

		*Set up the file to be written
		file open  settingsfile using "`textfile'", text write replace

		if "`quietdo'" != "" file write settingsfile "quietly {" _n

		*Write the standard message in the opening file.
		file write settingsfile ///
																		_n ///
			`" 	/*************************************************"' 	_n ///
			`" 	This file is was prepared by command ieboilstart "' 	_n ///
			`" 	on `date'."'											_n ///
																		_n ///
			`" 	Run this file to set the recommended settings for "' 	_n ///
			`" 	this project to your instance of Stata"' 				_n ///
			`" 	*************************************************/"' 	_n ///
																		_n ///
																				_n ///
			"	*See help file for ieboilstart for details on these settings"	_n ///
			_n

		** Write each setting prepared above to the do-file.
		while ("`return_string'" != "") {

			*Tokenize the first setting
			gettoken setting return_string : return_string, parse(";")

			*Write it to file
			file write settingsfile "	`setting'" _n

			*Remove the parse character
			local return_string = subinstr("`return_string'",";","",1)
		}

		if "`quietdo'" != "" file write settingsfile "}" _n

		*Close file
		file close settingsfile

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

			Create return value and output message

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

		return local ieboil_dofile "`textfile'"

		noi di "{phang}Due to how settings in Stata work, a command can not change any settings itself. ieboilstart therefore only prepares a do-file with all recommended settings. See {help ieboilstart :help ieboilstart} for how to apply the settings ieboilstart has prepared. If you do not run the do-file, ieboilstart does not affect any of your settings.{p_end}"
		noi di ""
		noi di "{phang}Do-file with all settings saved to: `textfile'{p_end}"
	}
	end

	*! version 2.0 24Oct2016  Kristoffer Bjarkefur kbjarkefur@worldbank.org

	capture program drop ieboilstart
	program ieboilstart , rclass

	qui {

		syntax using ,  Versionnumber(string) [noclear maxvar(numlist) matsize(numlist) QUIetdo CUSTom(string) ]

		*Potentional updates
		*set trace on
		*set scrollbufsize #

		version 10.0
		version `versionnumber'

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

			Check file name

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

		*Parse out the file name
		tokenize `"`using'"'
		local 		textfile "`2'"

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

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

		*If no file format suffix is specified, use the default .xlsx
		if "`file_suffix'" == "" {

			local textfile `"`textfile'.do"'
		}

		*If a file format suffix is specified make sure that it is one of the two allowed.
		else if "`file_suffix'" != ".do" {

			noi display as error "{phang}The file format specified in using is other than .do. Only the .do format is allowed. If no format is specified .do is assumed.{p_end}"
			error 198
		}

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

			Check version

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

		if "`maxvar'" != "" & "`clear'" != "" {

			di as error "{phang}It is not possible to set allowed maximum numbers of variables without clearing the data. noclear and maxvar() can therefore not be specified at the same time{p_end}"
			di ""
			error 198
			exit
		}

		local stata_versions "10.0 10.1 11.0 11.1 11.2 12.0 12.1 13.0 13.1 14.0 14.1"

		if `:list versionnumber in stata_versions' == 0 {

			di as error "{phang}Only recent major releases are allowed. One decimal must always be included. The releases currently allowed are:{break}`stata_versions'{p_end}"
			di ""
			error 198
			exit
		}

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

			Check input for maxvar and matsize
			if specified, other wise set
			maximum value allowed.

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

		local stata_types ic se mp
		foreach maxlocal in maxvar matsize {

			if "`maxlocal'" == "maxvar" {
				if c(MP) == 1 | c(SE) == 1 {
					local max 32767
					local min 2048
				}
				else {
					local max 2047
					local min 2047
				}
			}

			if "`maxlocal'" == "matsize" {
				if c(MP) == 1 | c(SE) == 1 {
					local max 11000
					local min 10
				}
				else {
					local max 800
					local min 10
				}
			}

			if c(MP) == 1 | c(SE) == 1 {
				local vusing "Stata SE and Stata MP"
			}
			else {
				local vusing "Stata IC"
			}

			*Test if user set maxvar
			if "``maxlocal''" != "" {

				if ``maxlocal'' >= `min' & ``maxlocal'' <= `max' {

					local `maxlocal' ``maxlocal''
				}
				else {

					di as error "{phang}`maxlocal' must be between `min' and `max' (inclusive) if you are using `vusing'. You entered ``maxlocal''.{p_end}"
					if ``maxlocal'' < `min' {
						di ""
						error 910
					}
					else {
						di ""
						error 912
					}
					exit
				}
			}
			else {

				*User did not specify value, use max value allowed
				local `maxlocal' `max'
			}
		}

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

			Prepare a local with all settings

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

		local return_string ""

		*Set verison number
		local return_string `return_string' version `versionnumber' ;

		*Set basic memory limits
		if "`clear'" == "" {
			local return_string `return_string' clear all ;
			local return_string `return_string' set maxvar 	`maxvar' ;
		}

		local return_string `return_string'  set matsize 	`matsize' ;

		*Set advanced memory limits
		local return_string `return_string' set niceness	5 ;

		if "`clear'" == "" {
			*These settings cannot be modified with data in memory
			local return_string `return_string' set min_memory	0 ;
			local return_string `return_string' set max_memory	. ;

			if c(bit) == 64 {
				local return_string `return_string' set segmentsize	32m ;
			}
			else {
				local return_string `return_string' set segmentsize	16m ;
			}
		}

		*Set default options
		local return_string `return_string' set more 		off ;
		local return_string `return_string' pause 			on 	;
		local return_string `return_string' set varabbrev 	off ;

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

			Add custom lines of code

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

		if "`custom'" != "" {

			*Add a comment that list where
			local return_string `return_string' ** The lines of code below was added manually when iboilstart ;
			local return_string `return_string' *  was used to create  this do-file. If there are any issues ;
			local return_string `return_string' *  with the code below, please reconfigure ieboilstart and ;
			local return_string `return_string' *  generate a new do-file. ;

			*Create a local with the rowlabel input to be tokenized
			local custom_code_lines `custom'

			while "`custom_code_lines'" != "" {

				*Parsing name and label pair
				gettoken code_line custom_code_lines : custom_code_lines, parse("@")

				*Removing leadning or trailing spaces
				local code_line = trim("`code_line'")

				local return_string `return_string' `code_line';

				*Parse char is not removed by gettoken
				local custom_code_lines = subinstr("`custom_code_lines'" ,"@","",1)
			}
		}

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

			Create the do-file

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

		*Create a local with the date
		local		date = subinstr(c(current_date)," ","",.)

		*Write the title rows defined above
		capture file close settingsfile

		*Set up the file to be written
		file open  settingsfile using "`textfile'", text write replace

		if "`quietdo'" != "" file write settingsfile "quietly {" _n

		*Write the standard message in the opening file.
		file write settingsfile ///
																		_n ///
			`" 	/*************************************************"' 	_n ///
			`" 	This file is was prepared by command ieboilstart "' 	_n ///
			`" 	on `date'."'											_n ///
																		_n ///
			`" 	Run this file to set the recommended settings for "' 	_n ///
			`" 	this project to your instance of Stata"' 				_n ///
			`" 	*************************************************/"' 	_n ///
																		_n ///
																				_n ///
			"	*See help file for ieboilstart for details on these settings"	_n ///
			_n

		** Write each setting prepared above to the do-file.
		while ("`return_string'" != "") {

			*Tokenize the first setting
			gettoken setting return_string : return_string, parse(";")

			*Write it to file
			file write settingsfile "	`setting'" _n

			*Remove the parse character
			local return_string = subinstr("`return_string'",";","",1)
		}

		if "`quietdo'" != "" file write settingsfile "}" _n

		*Close file
		file close settingsfile

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

			Create return value and output message

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

		return local ieboil_dofile "`textfile'"

		noi di "{phang}Due to how settings in Stata work, a command can not change any settings itself. ieboilstart therefore only prepares a do-file with all recommended settings. See {help ieboilstart :help ieboilstart} for how to apply the settings ieboilstart has prepared. If you do not run the do-file, ieboilstart does not affect any of your settings.{p_end}"
		noi di ""
		noi di "{phang}Do-file with all settings saved to: `textfile'{p_end}"
	}
	end


removed baseball

removed baseball

program reg_traf

local ln `1'
di "this is `ln'"

local int 1.treat#1.post#1.isbaseball
local x1 1.treat#1.post
local x2 1.isbaseball#1.post

est clear
use ${stash}master, clear

eststo: qui reg `ln'traf `x1' i.${fe} if isb==1, vce(robust)
qui estadd local fixed "No"
qui estadd local yearfe "Yes"

eststo: qui xtreg `ln'traf `x1' i.${fe} if isb==1, fe vce(robust)
qui estadd local fixed "Yes"
qui estadd local yearfe "Yes"

eststo: qui xtreg `ln'traf `x1' i.qy if isb==1, fe vce(robust)
qui estadd local fixed "Quality X Year"
qui estadd local yearfe "Yes"

/* eststo: qui xtreg `ln'traf `int' `x1' `x2' i.$fe, fe vce(robust) */
/* qui estadd local fixed "Yes" */
/* qui estadd local yearfe "Yes" */

/* eststo: qui xtreg `ln'traf `int' `x1' `x2' i.qy, fe vce(robust) */
/* qui estadd local fixed "Quality X Year" */
/* qui estadd local yearfe "Yes" */

esttab using "${tables}`ln'traf.tex", keep(`x1') se ar2 nonotes star(+ 0.15 * 0.10 ** 0.05 *** 0.01) coeflabels(`x1' "\emph{out-of-copy X post}") order(`x1') mtitles( "Traffic" "Traffic" "Traffic") replace booktabs s(fixed yearfe r2_a N, label("Player FE" "Year FE" "adj. \$R^2\$" N )) width(\hsize) staraux

end

program reg_traf

local ln `1'
di "this is `ln'"

local int 1.treat#1.post#1.isbaseball
local x1 1.treat#1.post
local x2 1.isbaseball#1.post

est clear
use ${stash}master, clear

eststo: qui xtreg `ln'traf `x1' i.${fe} if isb==1, fe vce(robust)
qui estadd local fixed "Yes"
qui estadd local yearfe "Yes"

eststo: qui xtreg `ln'traf `x1' i.qy if isb==1, fe vce(robust)
qui estadd local fixed "Quality X Year"
qui estadd local yearfe "Yes"

eststo: qui xtreg `ln'traf `int' `x1' `x2' i.$fe, fe vce(robust)
qui estadd local fixed "Yes"
qui estadd local yearfe "Yes"

eststo: qui xtreg `ln'traf `int' `x1' `x2' i.qy, fe vce(robust)
qui estadd local fixed "Quality X Year"
qui estadd local yearfe "Yes"

esttab using "${tables}`ln'traf.tex", keep(`int' `x1' `x2') se ar2 nonotes star(+ 0.15 * 0.10 ** 0.05 *** 0.01) coeflabels(`int' "\emph{out-of-copy X post X baseball}" `x1' "\emph{out-of-copy X post}" `x2' "\emph{baseball X post}") order(`int' `x1' `x2') mtitles( "Traffic" "Traffic" "Traffic" "Traffic") replace booktabs s(fixed yearfe r2_a N N_g, label("Player FE" "Year FE" "adj. \$R^2\$" N "Clusters")) width(\hsize) staraux

end


bugfix

bugfix

was returning a rowvec instead of a vec if y had only one col

// --------------------------------------------------------------------------
// FixedEffects main class
// --------------------------------------------------------------------------

mata:

class FixedEffects
{
    // Factors
    `Integer'               G                   // Number of sets of FEs
    `Integer'               N                   // number of obs
    `Factors'               factors
    `Vector'                sample
    `Varlist'               absvars
    `Varlist'               ivars
    `Varlist'               cvars
    `Boolean'               has_intercept
    `RowVector'             intercepts
    `RowVector'             num_slopes
    `Integer'               num_singletons
    `Boolean'               save_any_fe
    `Boolean'               save_all_fe
    `Varlist'               targets
    `RowVector'             save_fe

    // Optimization options
    `Real'                  tolerance
    `Integer'               maxiter
    `String'                transform           // Kaczmarz Cimmino Symmetric_kaczmarz (k c s)
    `String'                acceleration        // Acceleration method. None/No/Empty is none\
    `Integer'               accel_start         // Iteration where we start to accelerate // set it at 6? 2?3?
    `string'                slope_method
    `Boolean'               prune               // Whether to recursively prune degree-1 edges
    `Boolean'               abort               // Raise error if convergence failed?
    `Integer'               accel_freq          // Specific to Aitken's acceleration
    `Boolean'               storing_alphas      // 1 if we should compute the alphas/fes

    // Optimization objects
    `BipartiteGraph'        bg                  // Used when pruning 1-core vertices
    `Vector'                pruned_weight       // temp. weight for the factors that were pruned
    `Integer'               prune_g1            // Factor 1/2 in the bipartite subgraph that gets pruned
    `Integer'               prune_g2            // Factor 2/2 in the bipartite subgraph that gets pruned

    // Misc
    `Integer'               verbose
    `Boolean'               timeit

    // Weight-specific
    `Boolean'               has_weights
    `Variabe'               weight              // unsorted weight
    `String'                weight_var          // Weighting variable
    `String'                weight_type         // Weight type (pw, fw, etc)

    // Absorbed degrees-of-freedom computations
    `Integer'               G_extended          // Number of intercepts plus slopes
    `Integer'               df_a_redundant      // e(mobility)
    `Integer'               df_a_initial
    `Integer'               df_a                // df_a_inital - df_a_redundant
    `Vector'                doflist_M
    `Vector'                doflist_K
    `Vector'                doflist_M_is_exact
    `Vector'                doflist_M_is_nested
    `Vector'                is_slope
    `Integer'               df_a_nested // Redundant due to bein nested; used for: r2_a r2_a_within rmse

    // VCE and cluster variables
    `String'                vcetype
    `Integer'               num_clusters
    `Varlist'               clustervars
    `Varlist'               base_clustervars
    `String'                vceextra

    // Regression-specific
    `String'                original_varlist    // y x1 x2 (x3 x4 = z1 z2 z3)
    `String'                varlist             // y x1 x2 x3 x4 z1 z2 z3
    `String'                original_depvar
    `String'                original_indepvars
    `String'                original_endogvars
    `String'                original_instruments
    `String'                depvar              // y
    `String'                indepvars           // x1 x2
    `String'                endogvars           // x3 x4
    `String'                instruments         // z1 z2 z3

    `Boolean'               drop_singletons
    `String'                absorb              // contents of absorb()
    `String'                select_if           // If condition
    `String'                select_in           // In condition
    `String'                model               // ols, iv
    `String'                summarize_stats
    `Boolean'               summarize_quietly
    `StringRowVector'       dofadjustments // firstpair pairwise cluster continuous
    `Varname'               groupvar
    `String'                residuals
    `String'                diopts

    // Output
    `String'                cmdline
    `String'                subcmd
    `String'                title
    `Boolean'               converged
    `Integer'               iteration_count // e(ic)
    `Varlist'               extended_absvars
    `String'                notes
    `String'                equation_d
    `Integer'               df_r
    `Integer'               df_m
    `Integer'               N_clust
    `Integer'               N_clust_list
    `Real'                  rss
    `Real'                  rmse
    `Real'                  F
    `Real'                  tss
    `Real'                  tss_within
    `Real'                  sumweights
    `Real'                  r2
    `Real'                  r2_within
    `Real'                  r2_a
    `Real'                  r2_a_within
    `Real'                  ll
    `Real'                  ll_0

    // Methods
    `Void'                  new()
    `Matrix'                partial_out()
    `Void'                  _partial_out()
    `Variables'             project_one_fe()
    `Void'                  prune_1core()
    `Void'                  _expand_1core()
    `Void'                  estimate_dof()
    `Void'                  save_touse()
    `Void'                  store_alphas()
    `Void'                  save_variable()
    `Void'                  post_footnote()
    `Void'                  post()
}

// Set default value of properties
`Void' FixedEffects::new()
{
    num_singletons = .
    sample = J(0, 1, .)
    weight = 1 // set to 1 so cross(x, S.weight, y)==cross(x, y)

    verbose = 0

    // Optimization defaults
    slope_method = "invsym"
    maxiter = 1e4
    tolerance = 1e-8
    transform = "symmetric_kaczmarz"
    acceleration = "conjugate_gradient"
    accel_start = 6

    prune = 1
    converged = 0
    abort = 1
    storing_alphas = 0

    // Specific to Aitken:
    accel_freq = 3
}

`Variables' FixedEffects::partial_out(`Anything' data, | `Boolean' save_tss)
{
    // -data- is either a varlist or a matrix
    `Variables'             y
    `Varlist'               vars
    `RowVector'             idx
    `Integer'               i

    if (eltype(data) == "string") {
        vars = tokens(invtokens(data))
        if (verbose > 0) printf("\n{txt} ## Partialling out %g variables: {res}%s{txt}\n\n", cols(vars), invtokens(vars))
        if (verbose > 0) printf("{txt}    - Loading variables into Mata\n")
        y = st_data(sample, invtokens(vars))

        if (cols(y) < cols(vars)) {
            printf("{err}(some columns were dropped due to a bug/quirk in st_data() (%g cols created instead of %g for %s); running slower workaround)\n", cols(y), cols(vars), invtokens(vars))
            assert(0) // when does this happen?
        }
        else if (cols(y) > cols(vars)) {
            printf("{err}(some empty columns were added due to a bug/quirk in {bf:st_data()}; %g cols created instead of %g for {it:%s}; running slower workaround)\n", cols(y), cols(vars), invtokens(vars))
            y = J(rows(y), 0, .)
            for (i=1; i<=cols(vars); i++) {
                y = y, st_data(sample, vars[i])
            }
        }

        assert_msg(cols(y)==cols(vars), "st_data() constructed more columns than expected")

        idx = `selectindex'(colmax(abs(colminmax(y))) :== 0)
        if (cols(idx) & verbose>-1) {
            printf("{err}WARNING: after demeaning, some variables are just zeros: %s\n", invtokens(vars[idx]))
        }

        _partial_out(y, save_tss)
    }
    else {
        if (verbose > 0) printf("\n{txt} ## Partialling out %g variables\n\n", cols(data))
        _partial_out(y=data, save_tss)
    }
    //if (verbose==0) printf("\n")
    if (verbose==0) printf("{txt}(converged in %s iterations)\n", strofreal(iteration_count))
    return(y)
}

`Void' FixedEffects::store_alphas(`Varname' d_varname)
{
    `Integer'               g, i, j
    `StringRowVector'       varlabel
    `Variable'              d
    `RowVector'             tmp_stdev

    if (verbose > 0) printf("\n{txt} ## Storing estimated fixed effects\n\n")

    // Load -d- variable
    if (verbose > 0) printf("{txt}    - Loading d = e(depvar) - xb - e(resid)\n")
    d = st_data(sample, d_varname)
    assert(!missing(d))

    // Create empty alphas
    if (verbose > 0) printf("{txt}    - Initializing alphas\n")
    for (g=j=1; g<=G; g++) {
        if (!save_fe[g]) continue
        asarray(factors[g].extra, "alphas", J(factors[g].num_levels, intercepts[g] + num_slopes[g], 0))
        asarray(factors[g].extra, "tmp_alphas", J(factors[g].num_levels, intercepts[g] + num_slopes[g], 0))
    }

    // Fill out alphas
    if (verbose > 0) printf("{txt}    - Computing alphas\n")
    storing_alphas = 1
    d = accelerate_sd(this, d, &transform_kaczmarz())
    storing_alphas = 0

    if (verbose > 0) printf("{txt}    - SSR of d wrt FEs: %g\n", quadcross(d,d))

    // Store alphas in dataset
    if (verbose > 0) printf("{txt}    - Creating varlabels\n")
    for (g=j=1; g<=G; g++) {
        if (!save_fe[g]) {
            j = j + intercepts[g] + num_slopes[g]
            continue
        }
        varlabel = J(1, intercepts[g] + num_slopes[g], "")
        for (i=1; i<=cols(varlabel); i++) {
            varlabel[i] = sprintf("[FE] %s", extended_absvars[j])
            j++
        }

        if (num_slopes[g]) {
            if (verbose > 0) printf("{txt}    - Recovering unstandardized variables\n")
            tmp_stdev = asarray(factors[g].extra, "x_stdevs")
            if (intercepts[g]) tmp_stdev = 1, tmp_stdev

            // We need to *divide* the coefs by the stdev, not multiply!
            asarray(factors[g].extra, "alphas",
                asarray(factors[g].extra, "alphas") :/ tmp_stdev
            )
        }

        if (verbose > 0) printf("{txt}    - Storing alphas in dataset\n")
        save_variable(targets[g], asarray(factors[g].extra, "alphas")[factors[g].levels, .], varlabel)
        asarray(factors[g].extra, "alphas", .)
        asarray(factors[g].extra, "tmp_alphas", .)
    }
}

`Void' FixedEffects::_partial_out(`Variables' y, | `Boolean' save_tss)
{
    `RowVector'             stdevs, needs_zeroing
    `FunctionP'             funct_transform, func_accel // transform
    `Real'                  y_mean

    if (args()<2 | save_tss==.) save_tss = 0

    // Solver Warnings
    if (transform=="kaczmarz" & acceleration=="conjugate_gradient") {
        printf("{err}(WARNING: convergence is {bf:unlikely} with transform=kaczmarz and accel=CG)\n")
    }

    // Load transform pointer
    if (transform=="cimmino") funct_transform = &transform_cimmino()
    if (transform=="kaczmarz") funct_transform = &transform_kaczmarz()
    if (transform=="symmetric_kaczmarz") funct_transform = &transform_sym_kaczmarz()
    if (transform=="random_kaczmarz") funct_transform = &transform_rand_kaczmarz()

    // Pointer to acceleration routine
    if (acceleration=="test") func_accel = &accelerate_test()
    if (acceleration=="none") func_accel = &accelerate_none()
    if (acceleration=="conjugate_gradient") func_accel = &accelerate_cg()
    if (acceleration=="steepest_descent") func_accel = &accelerate_sd()
    if (acceleration=="aitken") func_accel = &accelerate_aitken()
    if (acceleration=="hybrid") func_accel = &accelerate_hybrid()

    // Shortcut for trivial case (1 FE)
    if (G==1) func_accel = &accelerate_none()

    // Compute TSS of depvar
    if (save_tss & tss==.) {
        if (has_intercept) {
            y_mean = mean(y[., 1], weight)
            tss = crossdev(y[., 1], y_mean, weight, y[., 1], y_mean) // Sum of w[i] * (y[i]-y_mean) ^ 2
        }
        else {
            tss = cross(y[., 1], weight, y[., 1]) // Sum of w[i] * y[i] ^ 2
        }
        if (weight_type=="aweight" | weight_type=="pweight") tss = tss * rows(y) / sum(weight)
    }

    // Standardize variables
    if (verbose > 0) printf("{txt}    - Standardizing variables\n")
    stdevs = reghdfe_standardize(y)

    // Solve
    if (verbose>0) printf("{txt}    - Running solver (acceleration={res}%s{txt}, transform={res}%s{txt} tol={res}%-1.0e{txt})\n", acceleration, transform, tolerance)
    if (verbose==1) printf("{txt}    - Iterating:")
    if (verbose>1) printf("{txt}      ")
    converged = 0
    iteration_count = 0
    y = (*func_accel)(this, y, funct_transform) :* stdevs
    // converged gets updated by check_convergence()

    if (prune) {
        _expand_1core(y)
    }

    // Standardizing makes it hard to detect values that are perfectly collinear with the absvars
    // in which case they should be 0.00 but they end up as 1e-16
    // EG: reghdfe price ibn.foreign , absorb(foreign)

    // This will edit to zero entire columns where *ALL* values are very close to zero
    needs_zeroing = colmax(abs(colminmax(y))) :< 1e-8 // chose something relatively close to epsilon() ~ 1e-16
    needs_zeroing = `selectindex'(needs_zeroing)
    if (cols(needs_zeroing)) {
        y[., needs_zeroing] = J(rows(y), cols(needs_zeroing), 0)
    }
}

`Variables' FixedEffects::project_one_fe(`Variables' y, `Integer' g)
{
    `Factor'                f
    `Boolean'               store_these_alphas
    `Matrix'                alphas, proj_y

    // Cons+K+W, Cons+K, K+W, K, Cons+W, Cons = 6 variants

    f = factors[g]
    store_these_alphas = storing_alphas & save_fe[g]
    if (store_these_alphas) assert(cols(y)==1)

    if (num_slopes[g]==0) {
        if (store_these_alphas) {
            alphas = panelmean(f.sort(y), f)
            asarray(factors[g].extra, "tmp_alphas", alphas)
            return(alphas[f.levels, .])
        }
        else {
            return(panelmean(f.sort(y), f)[f.levels, .])
        }
    }
    else {
        // This includes both cases, with and w/out intercept (## and #)
        if (store_these_alphas) {
            alphas = J(f.num_levels, intercepts[g] + num_slopes[g], .)
            proj_y = panelsolve_invsym(f.sort(y), f, intercepts[g], alphas)
            asarray(factors[g].extra, "tmp_alphas", alphas)
            return(proj_y)
        }
        else {
            return(panelsolve_invsym(f.sort(y), f, intercepts[g]))
        }
    }
}

`Void' FixedEffects::estimate_dof()
{
    `Boolean'               has_int
    `Integer'               g, h                        // index FEs (1..G)
    `Integer'               num_intercepts              // Number of absvars with an intercept term
    `Integer'               i_cluster, i_intercept, j_intercept
    `Integer'               i                           // index 1..G_extended
    `Integer'               j
    `Integer'               bg_verbose                  // verbose level when calling BipartiteGraph()
    `Integer'               m                           // Mobility groups between a specific pair of FEs
    `RowVector'             SubGs
    `RowVector'             offsets, idx, zeros, results
    `Matrix'                tmp
    `Variables'             data
    `DataCol'               cluster_data
    `String'                absvar, clustervar
    `Factor'                F
    `BipartiteGraph'        BG
    `Integer'               pair_count

    if (verbose > 0) printf("\n{txt} ## Estimating degrees-of-freedom absorbed by the fixed effects\n\n")

    // Count all FE intercepts and slopes
    SubGs = intercepts + num_slopes
    G_extended = sum(SubGs)
    num_intercepts = sum(intercepts)
    offsets = runningsum(SubGs) - SubGs :+ 1 // start of each FE within the extended list
    idx = `selectindex'(intercepts) // Select all FEs with intercepts
    if (verbose > 0) printf("{txt}    - there are %f fixed intercepts and slopes in the %f absvars\n", G_extended, G)

    // Initialize result vectors and scalars
    doflist_M_is_exact = J(1, G_extended, 0)
    doflist_M_is_nested = J(1, G_extended, 0)
    df_a_nested = 0

    // (1) M will hold the redundant coefs for each extended absvar (G_extended, not G)
    doflist_M = J(1, G_extended, 0)
    assert(0 <= num_clusters & num_clusters <= 10)
    if (num_clusters > 0 & anyof(dofadjustments, "clusters")) {

        // (2) (Intercept-Only) Look for absvars that are clustervars
        for (i_intercept=1; i_intercept<=length(idx); i_intercept++) {
            g = idx[i_intercept]
            i = offsets[g]
            absvar = invtokens(tokens(ivars[g]), "#")
            if (anyof(clustervars, absvar)) {
                doflist_M[i] = factors[g].num_levels
                df_a_nested = df_a_nested + doflist_M[i]
                doflist_M_is_exact[i] = doflist_M_is_nested[i] = 1
                idx[i_intercept] = 0
                if (verbose > 0) printf("{txt} - categorical variable {res}%s{txt} is also a cluster variable, so it doesn't reduce DoF\n", absvar)
            }
        }
        idx = select(idx, idx)

        // (3) (Intercept-Only) Look for absvars that are nested within a clustervar
        for (i_cluster=1; i_cluster<= num_clusters; i_cluster++) {
            cluster_data = .
            if (!length(idx)) break // no more absvars to process
            for (i_intercept=1; i_intercept<=length(idx); i_intercept++) {

                g = idx[i_intercept]
                i = offsets[g]
                absvar = invtokens(tokens(ivars[g]), "#")
                clustervar = clustervars[i_cluster]
                if (doflist_M_is_exact[i]) continue // nothing to do

                if (cluster_data == .) {
                    if (strpos(clustervar, "#")) {
                        clustervar = base_clustervars[i_cluster]
                        F = factor(clustervar, sample, ., "", 0, 0, ., 0)
                        cluster_data = F.levels
                        F = Factor() // clear
                    }
                    else {
                        cluster_data = __fload_data(clustervar, sample)
                    }
                }

                if (factors[g].nested_within(cluster_data)) {
                    doflist_M[i] = factors[g].num_levels
                    doflist_M_is_exact[i] = doflist_M_is_nested[i] = 1
                    df_a_nested = df_a_nested + doflist_M[i]
                    idx[i_intercept] = 0
                    if (verbose > 0) printf("{txt} - categorical variable {res}%s{txt} is nested within a cluster variable, so it doesn't reduce DoF\n", absvar)
                }
            }
            idx = select(idx, idx)
        }
        cluster_data = . // save memory
    } // end of the two cluster checks (absvar is clustervar; absvar is nested within clustervar)

    // (4) (Intercept-Only) Every intercept but the first has at least one redundant coef.
    if (length(idx) > 1) {
        if (verbose > 0) printf("{txt}    - there is at least one redundant coef. for every set of FE intercepts after the first one\n")
        doflist_M[offsets[idx[2..num_intercepts]]] = J(1, num_intercepts-1, 1) // Set DoF loss of all intercepts but the first one to 1
    }

    // (5) (Intercept-only) Mobility group algorithm
    // Excluding those already solved, the first absvar is exact

    if (length(idx)) {
        i = idx[1]
        doflist_M_is_exact[i] = 1
    }

    // Compute number of dijsoint subgraphs / mobility groups for each pair of remaining FEs
    if (anyof(dofadjustments, "firstpair") | anyof(dofadjustments, "pairwise")) {
        BG = BipartiteGraph()
        bg_verbose = max(( verbose - 1 , 0 ))
        pair_count = 0

        for (i_intercept=1; i_intercept<=length(idx)-1; i_intercept++) {
            for (j_intercept=i_intercept+1; j_intercept<=length(idx); j_intercept++) {
                g = idx[i_intercept]
                h = idx[j_intercept]
                i = offsets[h]
                BG.init(&factors[g], &factors[h], bg_verbose)
                m = BG.init_zigzag()
                ++pair_count
                if (verbose > 0) printf("{txt}    - mobility groups between FE intercepts #%f and #%f: {res}%f\n", g, h, m)
                doflist_M[i] = max(( doflist_M[i] , m ))
                if (j_intercept==2) doflist_M_is_exact[i] = 1
                if (pair_count & anyof(dofadjustments, "firstpair")) break
            }
            if (pair_count & anyof(dofadjustments, "firstpair")) break
        }
        BG = BipartiteGraph() // clear
    }
    // TODO: add group3hdfe

    // (6) See if cvars are zero (w/out intercept) or just constant (w/intercept)
    if (anyof(dofadjustments, "continuous")) {
        for (i=g=1; g<=G; g++) {
            // If model has intercept, redundant cvars are those that are CONSTANT
            // Without intercept, a cvar has to be zero within a FE for it to be redundant
            // Since S.fes[g].x are already demeaned IF they have intercept, we don't have to worry about the two cases
            has_int = intercepts[g]
            if (has_int) i++
            if (!num_slopes[g]) continue

            data = asarray(factors[g].extra, "x")
            assert(num_slopes[g]==cols(data))
            results = J(1, cols(data), 0)
            // float(1.1) - 1 == 2.384e-08 , so let's pick something bigger, 1e-6
            zeros = J(1, cols(data), 1e-6)
            // This can be speed up by moving the -if- outside the -for-
            for (j = 1; j <= factors[g].num_levels; j++) {
                tmp = colminmax(panelsubmatrix(data, j, factors[g].info))
                if (has_int) {
                    results = results + ((tmp[2, .] - tmp[1, .]) :<= zeros)
                }
                else {
                    results = results + (colsum(abs(tmp)) :<= zeros)
                }
            }
            data = .
            if (sum(results)) {
                if (has_int  & verbose) printf("{txt}    - the slopes in the FE #%f are constant for {res}%f{txt} levels, which don't reduce DoF\n", g, sum(results))
                if (!has_int & verbose) printf("{txt}    - the slopes in the FE #%f are zero for {res}%f{txt} levels, which don't reduce DoF\n", g, sum(results))
                doflist_M[i..i+num_slopes[g]-1] = results
            }
            i = i + num_slopes[g]
        }
    }

    // Store results (besides doflist_..., etc.)
    doflist_K = J(1, G_extended, .)
    for (g=1; g<=G; g++) {
        i = offsets[g]
        j = g==G ? G_extended : offsets[g+1]
        doflist_K[i..j] = J(1, j-i+1, factors[g].num_levels)
    }
    df_a_initial = sum(doflist_K)
    df_a_redundant = sum(doflist_M)
    df_a = df_a_initial - df_a_redundant
}

`Void' FixedEffects::prune_1core()
{
    // Note that we can't prune degree-2 nodes, to keep the graph bipartite
    `Integer'               i, j, g
    `Vector'                subgraph_id

    `Vector'                idx
    `RowVector'             i_prune

    assert(G==2) // bugbug remove?

    // Abort if the user set HDFE.prune = 0
    if (!prune) return

    // Pick two factors, and check if we really benefit from pruning
    prune = 0
    i_prune = J(1, 2, 0)
    for (g=i=1; g<=2; g++) {
        //if (intercepts[g] & !num_slopes[g] & factors[g].num_levels>100) {
        if (intercepts[g] & !num_slopes[g]) {
            i_prune[i++] = g // increments at the end
            if (i > 2) { // success!
                prune = 1
                break
            }
        }
    }

    if (!prune) return

    // for speed, the factor with more levels goes first
    i = i_prune[1]
    j = i_prune[2]
    //if (factors[i].num_levels < factors[j].num_levels) swap(i, j) // bugbug uncomment it!
    prune_g1 = i
    prune_g2 = j

    bg = BipartiteGraph()
    bg.init(&factors[prune_g1], &factors[prune_g2], verbose)
    bg.init_zigzag(1) // 1 => save subgraphs into bg.subgraph_id
    bg.compute_cores()
    bg.prune_1core(weight)
}

// bugbug fix or remove this fn altogether
`Void' FixedEffects::_expand_1core(`Variables' y)
{
    y = bg.expand_1core(y)
}

`Void' FixedEffects::save_touse(`Varname' touse, | `Boolean' replace)
{
    `Integer'               idx
    `Vector'                mask

    if (verbose > 0) printf("\n{txt} ## Saving e(sample)\n")
    mask = J(st_nobs(), 1, 0)
    mask[sample] = J(rows(sample), 1, 1)

    if (args()<2 | replace==.) {
        // Generate
        idx = st_addvar("byte", touse)
        st_store(., idx, mask)
    }
    else {
        // Replace
        st_store(., touse, mask)
    }
}

`Void' FixedEffects::save_variable(`Varname' varname,
                                   `Variables' data,
                                 | `Varlist' varlabel)
{
    `RowVector'               idx
    `Integer'               i
    `Vector'                mask
    idx = st_addvar("double", tokens(varname))
    st_store(sample, idx, data)
    if (args()>=3 & varlabel!="") {
        for (i=1; i<=cols(data); i++) {
            st_varlabel(idx[i], varlabel[i])
        }
    }

}

`Void' FixedEffects::post_footnote()
{
    `Matrix'                table
    `StringVector'          rowstripe
    `StringRowVector'       colstripe
    `String'                text

    assert(!missing(G))
    st_numscalar("e(N_hdfe)", G)
    st_numscalar("e(N_hdfe_extended)", G_extended)
    st_numscalar("e(df_a)", df_a)
    st_numscalar("e(df_a_initial)", df_a_initial)
    st_numscalar("e(df_a_redundant)", df_a_redundant)
    st_numscalar("e(df_a_nested)", df_a_nested)

    st_global("e(absvars)", invtokens(absvars))
    text = invtokens(extended_absvars)
    text = subinstr(text, "1.", "")
    st_global("e(extended_absvars)", text)

    // Absorbed degrees-of-freedom table
    table = (doflist_K \ doflist_M \ (doflist_K-doflist_M) \ !doflist_M_is_exact \ doflist_M_is_nested)'
    rowstripe = extended_absvars'
    rowstripe = J(rows(table), 1, "") , extended_absvars' // add equation col
    colstripe = "Categories" \ "Redundant" \ "Num Coefs" \ "Exact?" \ "Nested?" // colstripe cannot have dots on Stata 12 or earlier
    colstripe = J(cols(table), 1, "") , colstripe // add equation col
    st_matrix("e(dof_table)", table)
    st_matrixrowstripe("e(dof_table)", rowstripe)
    st_matrixcolstripe("e(dof_table)", colstripe)

    st_numscalar("e(ic)", iteration_count)
    st_numscalar("e(drop_singletons)", drop_singletons)
    st_numscalar("e(num_singletons)", num_singletons)
}

`Void' FixedEffects::post(`Options' options)
{
    `String'        text
    `Integer'       i

    post_footnote()

    // ---- constants -------------------------------------------------------

    st_global("e(predict)", "reghdfe_p")
    st_global("e(estat_cmd)", "reghdfe_estat")
    st_global("e(footnote)", "reghdfe_footnote")
    st_global("e(marginsok)", "")
    st_numscalar("e(df_m)", df_m)

    assert(title != "")
    text = sprintf("HDFE %s", title)
    st_global("e(title)", text)

    text = sprintf("Absorbing %g HDFE %s", G, plural(G, "group"))
    st_global("e(title2)", text)

    st_global("e(model)", model)
    st_global("e(cmdline)", cmdline)

    st_numscalar("e(tss)", tss)
    st_numscalar("e(tss_within)", tss_within)
    st_numscalar("e(rss)", rss)
    st_numscalar("e(mss)", tss - rss)
    st_numscalar("e(rmse)", rmse)
    st_numscalar("e(F)", F)

    st_numscalar("e(ll)", ll)
    st_numscalar("e(ll_0)", ll_0)

    st_numscalar("e(r2)", r2)
    st_numscalar("e(r2_within)", r2_within)
    st_numscalar("e(r2_a)", r2_a)
    st_numscalar("e(r2_a_within)", r2_a_within)

    if (!missing(N_clust)) {
        st_numscalar("e(N_clust)", N_clust)
        for (i=1; i<=num_clusters; i++) {
            text = sprintf("e(N_clust%g)", i)
            st_numscalar(text, N_clust_list[i])
        }
        text = "Statistics robust to heteroskedasticity"
        st_global("e(title3)", text)
    }

    if (!missing(sumweights)) st_numscalar("e(sumweights)", sumweights)

    // ---- .options properties ---------------------------------------------

    st_global("e(depvar)", depvar)
    st_global("e(indepvars)", indepvars)
    st_global("e(endogvars)", endogvars)
    st_global("e(instruments)", instruments)

    if (!missing(N_clust)) {
        st_numscalar("e(N_clustervars)", num_clusters)
        st_global("e(clustvar)", invtokens(clustervars))
        for (i=1; i<=num_clusters; i++) {
            text = sprintf("e(clustvar%g)", i)
            st_global(text, clustervars[i])
        }
    }

    if (residuals != "") {
        st_global("e(resid)", residuals)
    }

    // Stata uses e(vcetype) for the SE column headers
    // In the default option, leave it empty.
    // In the cluster and robust options, set it as "Robust"
    text = strproper(vcetype)
    if (text=="Cluster") text = "Robust"
    if (text=="Unadjusted") text = ""
    assert(anyof( ("", "Robust", "Jackknife", "Bootstrap") , text))
    if (text!="") st_global("e(vcetype)", text)

    text = vcetype
    if (text=="unadjusted") text = "ols"
    st_global("e(vce)", text)

    // Weights
    if (weight_type != "") {
        st_global("e(wexp)", "= " + weight_var)
        st_global("e(wtype)", weight_type)
    }
}

end

// --------------------------------------------------------------------------
// FixedEffects main class
// --------------------------------------------------------------------------

mata:

class FixedEffects
{
    // Factors
    `Integer'               G                   // Number of sets of FEs
    `Integer'               N                   // number of obs
    `Factors'               factors
    `Vector'                sample
    `Varlist'               absvars
    `Varlist'               ivars
    `Varlist'               cvars
    `Boolean'               has_intercept
    `RowVector'             intercepts
    `RowVector'             num_slopes
    `Integer'               num_singletons
    `Boolean'               save_any_fe
    `Boolean'               save_all_fe
    `Varlist'               targets
    `RowVector'             save_fe

    // Optimization options
    `Real'                  tolerance
    `Integer'               maxiter
    `String'                transform           // Kaczmarz Cimmino Symmetric_kaczmarz (k c s)
    `String'                acceleration        // Acceleration method. None/No/Empty is none\
    `Integer'               accel_start         // Iteration where we start to accelerate // set it at 6? 2?3?
    `string'                slope_method
    `Boolean'               prune               // Whether to recursively prune degree-1 edges
    `Boolean'               abort               // Raise error if convergence failed?
    `Integer'               accel_freq          // Specific to Aitken's acceleration
    `Boolean'               storing_alphas      // 1 if we should compute the alphas/fes

    // Optimization objects
    `BipartiteGraph'        bg                  // Used when pruning 1-core vertices
    `Vector'                pruned_weight       // temp. weight for the factors that were pruned
    `Integer'               prune_g1            // Factor 1/2 in the bipartite subgraph that gets pruned
    `Integer'               prune_g2            // Factor 2/2 in the bipartite subgraph that gets pruned

    // Misc
    `Integer'               verbose
    `Boolean'               timeit

    // Weight-specific
    `Boolean'               has_weights
    `Variabe'               weight              // unsorted weight
    `String'                weight_var          // Weighting variable
    `String'                weight_type         // Weight type (pw, fw, etc)

    // Absorbed degrees-of-freedom computations
    `Integer'               G_extended          // Number of intercepts plus slopes
    `Integer'               df_a_redundant      // e(mobility)
    `Integer'               df_a_initial
    `Integer'               df_a                // df_a_inital - df_a_redundant
    `Vector'                doflist_M
    `Vector'                doflist_K
    `Vector'                doflist_M_is_exact
    `Vector'                doflist_M_is_nested
    `Vector'                is_slope
    `Integer'               df_a_nested // Redundant due to bein nested; used for: r2_a r2_a_within rmse

    // VCE and cluster variables
    `String'                vcetype
    `Integer'               num_clusters
    `Varlist'               clustervars
    `Varlist'               base_clustervars
    `String'                vceextra

    // Regression-specific
    `String'                original_varlist    // y x1 x2 (x3 x4 = z1 z2 z3)
    `String'                varlist             // y x1 x2 x3 x4 z1 z2 z3
    `String'                original_depvar
    `String'                original_indepvars
    `String'                original_endogvars
    `String'                original_instruments
    `String'                depvar              // y
    `String'                indepvars           // x1 x2
    `String'                endogvars           // x3 x4
    `String'                instruments         // z1 z2 z3

    `Boolean'               drop_singletons
    `String'                absorb              // contents of absorb()
    `String'                select_if           // If condition
    `String'                select_in           // In condition
    `String'                model               // ols, iv
    `String'                summarize_stats
    `Boolean'               summarize_quietly
    `StringRowVector'       dofadjustments // firstpair pairwise cluster continuous
    `Varname'               groupvar
    `String'                residuals
    `String'                diopts

    // Output
    `String'                cmdline
    `String'                subcmd
    `String'                title
    `Boolean'               converged
    `Integer'               iteration_count // e(ic)
    `Varlist'               extended_absvars
    `String'                notes
    `String'                equation_d
    `Integer'               df_r
    `Integer'               df_m
    `Integer'               N_clust
    `Integer'               N_clust_list
    `Real'                  rss
    `Real'                  rmse
    `Real'                  F
    `Real'                  tss
    `Real'                  tss_within
    `Real'                  sumweights
    `Real'                  r2
    `Real'                  r2_within
    `Real'                  r2_a
    `Real'                  r2_a_within
    `Real'                  ll
    `Real'                  ll_0

    // Methods
    `Void'                  new()
    `Matrix'                partial_out()
    `Void'                  _partial_out()
    `Variables'             project_one_fe()
    `Void'                  prune_1core()
    `Void'                  _expand_1core()
    `Void'                  estimate_dof()
    `Void'                  save_touse()
    `Void'                  store_alphas()
    `Void'                  save_variable()
    `Void'                  post_footnote()
    `Void'                  post()
}

// Set default value of properties
`Void' FixedEffects::new()
{
    num_singletons = .
    sample = J(0, 1, .)
    weight = 1 // set to 1 so cross(x, S.weight, y)==cross(x, y)

    verbose = 0

    // Optimization defaults
    slope_method = "invsym"
    maxiter = 1e4
    tolerance = 1e-8
    transform = "symmetric_kaczmarz"
    acceleration = "conjugate_gradient"
    accel_start = 6

    prune = 1
    converged = 0
    abort = 1
    storing_alphas = 0

    // Specific to Aitken:
    accel_freq = 3
}

`Variables' FixedEffects::partial_out(`Anything' data, | `Boolean' save_tss)
{
    // -data- is either a varlist or a matrix
    `Variables'             y
    `Varlist'               vars
    `RowVector'             idx
    `Integer'               i

    if (eltype(data) == "string") {
        vars = tokens(invtokens(data))
        if (verbose > 0) printf("\n{txt} ## Partialling out %g variables: {res}%s{txt}\n\n", cols(vars), invtokens(vars))
        if (verbose > 0) printf("{txt}    - Loading variables into Mata\n")
        y = st_data(sample, invtokens(vars))

        if (cols(y) < cols(vars)) {
            printf("{err}(some columns were dropped due to a bug/quirk in st_data() (%g cols created instead of %g for %s); running slower workaround)\n", cols(y), cols(vars), invtokens(vars))
            assert(0) // when does this happen?
        }
        else if (cols(y) > cols(vars)) {
            printf("{err}(some empty columns were added due to a bug/quirk in {bf:st_data()}; %g cols created instead of %g for {it:%s}; running slower workaround)\n", cols(y), cols(vars), invtokens(vars))
            y = J(rows(y), 0, .)
            for (i=1; i<=cols(vars); i++) {
                y = y, st_data(sample, vars[i])
            }
        }

        assert_msg(cols(y)==cols(vars), "st_data() constructed more columns than expected")

        idx = `selectindex'(colmax(abs(colminmax(y))) :== 0)
        if (cols(idx) & verbose>-1) {
            printf("{err}WARNING: after demeaning, some variables are just zeros: %s\n", invtokens(vars[idx]))
        }

        _partial_out(y, save_tss)
    }
    else {
        if (verbose > 0) printf("\n{txt} ## Partialling out %g variables\n\n", cols(data))
        _partial_out(y=data, save_tss)
    }
    //if (verbose==0) printf("\n")
    if (verbose==0) printf("{txt}(converged in %s iterations)\n", strofreal(iteration_count))
    return(y)
}

`Void' FixedEffects::store_alphas(`Varname' d_varname)
{
    `Integer'               g, i, j
    `StringRowVector'       varlabel
    `Variable'              d
    `RowVector'             tmp_stdev

    if (verbose > 0) printf("\n{txt} ## Storing estimated fixed effects\n\n")

    // Load -d- variable
    if (verbose > 0) printf("{txt}    - Loading d = e(depvar) - xb - e(resid)\n")
    d = st_data(sample, d_varname)
    assert(!missing(d))

    // Create empty alphas
    if (verbose > 0) printf("{txt}    - Initializing alphas\n")
    for (g=j=1; g<=G; g++) {
        if (!save_fe[g]) continue
        asarray(factors[g].extra, "alphas", J(factors[g].num_levels, intercepts[g] + num_slopes[g], 0))
        asarray(factors[g].extra, "tmp_alphas", J(factors[g].num_levels, intercepts[g] + num_slopes[g], 0))
    }

    // Fill out alphas
    if (verbose > 0) printf("{txt}    - Computing alphas\n")
    storing_alphas = 1
    d = accelerate_sd(this, d, &transform_kaczmarz())
    storing_alphas = 0

    if (verbose > 0) printf("{txt}    - SSR of d wrt FEs: %g\n", quadcross(d,d))

    // Store alphas in dataset
    if (verbose > 0) printf("{txt}    - Creating varlabels\n")
    for (g=j=1; g<=G; g++) {
        if (!save_fe[g]) {
            j = j + intercepts[g] + num_slopes[g]
            continue
        }
        varlabel = J(1, intercepts[g] + num_slopes[g], "")
        for (i=1; i<=cols(varlabel); i++) {
            varlabel[i] = sprintf("[FE] %s", extended_absvars[j])
            j++
        }

        if (num_slopes[g]) {
            if (verbose > 0) printf("{txt}    - Recovering unstandardized variables\n")
            tmp_stdev = asarray(factors[g].extra, "x_stdevs")
            if (intercepts[g]) tmp_stdev = 1, tmp_stdev

            // We need to *divide* the coefs by the stdev, not multiply!
            asarray(factors[g].extra, "alphas",
                asarray(factors[g].extra, "alphas") :/ tmp_stdev
            )
        }

        if (verbose > 0) printf("{txt}    - Storing alphas in dataset\n")
        save_variable(targets[g], asarray(factors[g].extra, "alphas")[factors[g].levels, .], varlabel)
        asarray(factors[g].extra, "alphas", .)
        asarray(factors[g].extra, "tmp_alphas", .)
    }
}

`Void' FixedEffects::_partial_out(`Variables' y, | `Boolean' save_tss)
{
    `RowVector'             stdevs, needs_zeroing
    `FunctionP'             funct_transform, func_accel // transform
    `Real'                  y_mean

    if (args()<2 | save_tss==.) save_tss = 0

    // Solver Warnings
    if (transform=="kaczmarz" & acceleration=="conjugate_gradient") {
        printf("{err}(WARNING: convergence is {bf:unlikely} with transform=kaczmarz and accel=CG)\n")
    }

    // Load transform pointer
    if (transform=="cimmino") funct_transform = &transform_cimmino()
    if (transform=="kaczmarz") funct_transform = &transform_kaczmarz()
    if (transform=="symmetric_kaczmarz") funct_transform = &transform_sym_kaczmarz()
    if (transform=="random_kaczmarz") funct_transform = &transform_rand_kaczmarz()

    // Pointer to acceleration routine
    if (acceleration=="test") func_accel = &accelerate_test()
    if (acceleration=="none") func_accel = &accelerate_none()
    if (acceleration=="conjugate_gradient") func_accel = &accelerate_cg()
    if (acceleration=="steepest_descent") func_accel = &accelerate_sd()
    if (acceleration=="aitken") func_accel = &accelerate_aitken()
    if (acceleration=="hybrid") func_accel = &accelerate_hybrid()

    // Shortcut for trivial case (1 FE)
    if (G==1) func_accel = &accelerate_none()

    // Compute TSS of depvar
    if (save_tss & tss==.) {
        if (has_intercept) {
            y_mean = mean(y[., 1], weight)
            tss = crossdev(y[., 1], y_mean, weight, y[., 1], y_mean) // Sum of w[i] * (y[i]-y_mean) ^ 2
        }
        else {
            tss = cross(y[., 1], weight, y[., 1]) // Sum of w[i] * y[i] ^ 2
        }
        if (weight_type=="aweight" | weight_type=="pweight") tss = tss * rows(y) / sum(weight)
    }

    // Standardize variables
    if (verbose > 0) printf("{txt}    - Standardizing variables\n")
    stdevs = reghdfe_standardize(y)

    // Solve
    if (verbose>0) printf("{txt}    - Running solver (acceleration={res}%s{txt}, transform={res}%s{txt} tol={res}%-1.0e{txt})\n", acceleration, transform, tolerance)
    if (verbose==1) printf("{txt}    - Iterating:")
    if (verbose>1) printf("{txt}      ")
    converged = 0
    iteration_count = 0
    y = (*func_accel)(this, y, funct_transform) :* stdevs
    // converged gets updated by check_convergence()

    if (prune) {
        _expand_1core(y)
    }

    // Standardizing makes it hard to detect values that are perfectly collinear with the absvars
    // in which case they should be 0.00 but they end up as 1e-16
    // EG: reghdfe price ibn.foreign , absorb(foreign)

    // This will edit to zero entire columns where *ALL* values are very close to zero
    needs_zeroing = colmax(abs(colminmax(y))) :< 1e-8 // chose something relatively close to epsilon() ~ 1e-16
    needs_zeroing = `selectindex'(needs_zeroing)
    if (cols(needs_zeroing)) {
        y[., needs_zeroing] = J(rows(y), cols(needs_zeroing), 0)
    }
}

`Variables' FixedEffects::project_one_fe(`Variables' y, `Integer' g)
{
    `Factor'                f
    `Boolean'               store_these_alphas
    `Matrix'                alphas, proj_y

    // Cons+K+W, Cons+K, K+W, K, Cons+W, Cons = 6 variants

    f = factors[g]
    store_these_alphas = storing_alphas & save_fe[g]
    if (store_these_alphas) assert(cols(y)==1)

    if (num_slopes[g]==0) {
        if (store_these_alphas) {
            alphas = panelmean(f.sort(y), f)
            asarray(factors[g].extra, "tmp_alphas", alphas)
            return(alphas[f.levels])
        }
        else {
            if (cols(y)==1) {
                return(panelmean(f.sort(y), f)[f.levels])
            }
            else {
                return(panelmean(f.sort(y), f)[f.levels, .])
            }
        }
    }
    else {
        // This includes both cases, with and w/out intercept (## and #)
        if (store_these_alphas) {
            alphas = J(f.num_levels, intercepts[g] + num_slopes[g], .)
            proj_y = panelsolve_invsym(f.sort(y), f, intercepts[g], alphas)
            asarray(factors[g].extra, "tmp_alphas", alphas)
            return(proj_y)
        }
        else {
            return(panelsolve_invsym(f.sort(y), f, intercepts[g]))
        }
    }
}

`Void' FixedEffects::estimate_dof()
{
    `Boolean'               has_int
    `Integer'               g, h                        // index FEs (1..G)
    `Integer'               num_intercepts              // Number of absvars with an intercept term
    `Integer'               i_cluster, i_intercept, j_intercept
    `Integer'               i                           // index 1..G_extended
    `Integer'               j
    `Integer'               bg_verbose                  // verbose level when calling BipartiteGraph()
    `Integer'               m                           // Mobility groups between a specific pair of FEs
    `RowVector'             SubGs
    `RowVector'             offsets, idx, zeros, results
    `Matrix'                tmp
    `Variables'             data
    `DataCol'               cluster_data
    `String'                absvar, clustervar
    `Factor'                F
    `BipartiteGraph'        BG
    `Integer'               pair_count

    if (verbose > 0) printf("\n{txt} ## Estimating degrees-of-freedom absorbed by the fixed effects\n\n")

    // Count all FE intercepts and slopes
    SubGs = intercepts + num_slopes
    G_extended = sum(SubGs)
    num_intercepts = sum(intercepts)
    offsets = runningsum(SubGs) - SubGs :+ 1 // start of each FE within the extended list
    idx = `selectindex'(intercepts) // Select all FEs with intercepts
    if (verbose > 0) printf("{txt}    - there are %f fixed intercepts and slopes in the %f absvars\n", G_extended, G)

    // Initialize result vectors and scalars
    doflist_M_is_exact = J(1, G_extended, 0)
    doflist_M_is_nested = J(1, G_extended, 0)
    df_a_nested = 0

    // (1) M will hold the redundant coefs for each extended absvar (G_extended, not G)
    doflist_M = J(1, G_extended, 0)
    assert(0 <= num_clusters & num_clusters <= 10)
    if (num_clusters > 0 & anyof(dofadjustments, "clusters")) {

        // (2) (Intercept-Only) Look for absvars that are clustervars
        for (i_intercept=1; i_intercept<=length(idx); i_intercept++) {
            g = idx[i_intercept]
            i = offsets[g]
            absvar = invtokens(tokens(ivars[g]), "#")
            if (anyof(clustervars, absvar)) {
                doflist_M[i] = factors[g].num_levels
                df_a_nested = df_a_nested + doflist_M[i]
                doflist_M_is_exact[i] = doflist_M_is_nested[i] = 1
                idx[i_intercept] = 0
                if (verbose > 0) printf("{txt} - categorical variable {res}%s{txt} is also a cluster variable, so it doesn't reduce DoF\n", absvar)
            }
        }
        idx = select(idx, idx)

        // (3) (Intercept-Only) Look for absvars that are nested within a clustervar
        for (i_cluster=1; i_cluster<= num_clusters; i_cluster++) {
            cluster_data = .
            if (!length(idx)) break // no more absvars to process
            for (i_intercept=1; i_intercept<=length(idx); i_intercept++) {

                g = idx[i_intercept]
                i = offsets[g]
                absvar = invtokens(tokens(ivars[g]), "#")
                clustervar = clustervars[i_cluster]
                if (doflist_M_is_exact[i]) continue // nothing to do

                if (cluster_data == .) {
                    if (strpos(clustervar, "#")) {
                        clustervar = base_clustervars[i_cluster]
                        F = factor(clustervar, sample, ., "", 0, 0, ., 0)
                        cluster_data = F.levels
                        F = Factor() // clear
                    }
                    else {
                        cluster_data = __fload_data(clustervar, sample)
                    }
                }

                if (factors[g].nested_within(cluster_data)) {
                    doflist_M[i] = factors[g].num_levels
                    doflist_M_is_exact[i] = doflist_M_is_nested[i] = 1
                    df_a_nested = df_a_nested + doflist_M[i]
                    idx[i_intercept] = 0
                    if (verbose > 0) printf("{txt} - categorical variable {res}%s{txt} is nested within a cluster variable, so it doesn't reduce DoF\n", absvar)
                }
            }
            idx = select(idx, idx)
        }
        cluster_data = . // save memory
    } // end of the two cluster checks (absvar is clustervar; absvar is nested within clustervar)

    // (4) (Intercept-Only) Every intercept but the first has at least one redundant coef.
    if (length(idx) > 1) {
        if (verbose > 0) printf("{txt}    - there is at least one redundant coef. for every set of FE intercepts after the first one\n")
        doflist_M[offsets[idx[2..num_intercepts]]] = J(1, num_intercepts-1, 1) // Set DoF loss of all intercepts but the first one to 1
    }

    // (5) (Intercept-only) Mobility group algorithm
    // Excluding those already solved, the first absvar is exact

    if (length(idx)) {
        i = idx[1]
        doflist_M_is_exact[i] = 1
    }

    // Compute number of dijsoint subgraphs / mobility groups for each pair of remaining FEs
    if (anyof(dofadjustments, "firstpair") | anyof(dofadjustments, "pairwise")) {
        BG = BipartiteGraph()
        bg_verbose = max(( verbose - 1 , 0 ))
        pair_count = 0

        for (i_intercept=1; i_intercept<=length(idx)-1; i_intercept++) {
            for (j_intercept=i_intercept+1; j_intercept<=length(idx); j_intercept++) {
                g = idx[i_intercept]
                h = idx[j_intercept]
                i = offsets[h]
                BG.init(&factors[g], &factors[h], bg_verbose)
                m = BG.init_zigzag()
                ++pair_count
                if (verbose > 0) printf("{txt}    - mobility groups between FE intercepts #%f and #%f: {res}%f\n", g, h, m)
                doflist_M[i] = max(( doflist_M[i] , m ))
                if (j_intercept==2) doflist_M_is_exact[i] = 1
                if (pair_count & anyof(dofadjustments, "firstpair")) break
            }
            if (pair_count & anyof(dofadjustments, "firstpair")) break
        }
        BG = BipartiteGraph() // clear
    }
    // TODO: add group3hdfe

    // (6) See if cvars are zero (w/out intercept) or just constant (w/intercept)
    if (anyof(dofadjustments, "continuous")) {
        for (i=g=1; g<=G; g++) {
            // If model has intercept, redundant cvars are those that are CONSTANT
            // Without intercept, a cvar has to be zero within a FE for it to be redundant
            // Since S.fes[g].x are already demeaned IF they have intercept, we don't have to worry about the two cases
            has_int = intercepts[g]
            if (has_int) i++
            if (!num_slopes[g]) continue

            data = asarray(factors[g].extra, "x")
            assert(num_slopes[g]==cols(data))
            results = J(1, cols(data), 0)
            // float(1.1) - 1 == 2.384e-08 , so let's pick something bigger, 1e-6
            zeros = J(1, cols(data), 1e-6)
            // This can be speed up by moving the -if- outside the -for-
            for (j = 1; j <= factors[g].num_levels; j++) {
                tmp = colminmax(panelsubmatrix(data, j, factors[g].info))
                if (has_int) {
                    results = results + ((tmp[2, .] - tmp[1, .]) :<= zeros)
                }
                else {
                    results = results + (colsum(abs(tmp)) :<= zeros)
                }
            }
            data = .
            if (sum(results)) {
                if (has_int  & verbose) printf("{txt}    - the slopes in the FE #%f are constant for {res}%f{txt} levels, which don't reduce DoF\n", g, sum(results))
                if (!has_int & verbose) printf("{txt}    - the slopes in the FE #%f are zero for {res}%f{txt} levels, which don't reduce DoF\n", g, sum(results))
                doflist_M[i..i+num_slopes[g]-1] = results
            }
            i = i + num_slopes[g]
        }
    }

    // Store results (besides doflist_..., etc.)
    doflist_K = J(1, G_extended, .)
    for (g=1; g<=G; g++) {
        i = offsets[g]
        j = g==G ? G_extended : offsets[g+1]
        doflist_K[i..j] = J(1, j-i+1, factors[g].num_levels)
    }
    df_a_initial = sum(doflist_K)
    df_a_redundant = sum(doflist_M)
    df_a = df_a_initial - df_a_redundant
}

`Void' FixedEffects::prune_1core()
{
    // Note that we can't prune degree-2 nodes, to keep the graph bipartite
    `Integer'               i, j, g
    `Vector'                subgraph_id

    `Vector'                idx
    `RowVector'             i_prune

    assert(G==2) // bugbug remove?

    // Abort if the user set HDFE.prune = 0
    if (!prune) return

    // Pick two factors, and check if we really benefit from pruning
    prune = 0
    i_prune = J(1, 2, 0)
    for (g=i=1; g<=2; g++) {
        //if (intercepts[g] & !num_slopes[g] & factors[g].num_levels>100) {
        if (intercepts[g] & !num_slopes[g]) {
            i_prune[i++] = g // increments at the end
            if (i > 2) { // success!
                prune = 1
                break
            }
        }
    }

    if (!prune) return

    // for speed, the factor with more levels goes first
    i = i_prune[1]
    j = i_prune[2]
    //if (factors[i].num_levels < factors[j].num_levels) swap(i, j) // bugbug uncomment it!
    prune_g1 = i
    prune_g2 = j

    bg = BipartiteGraph()
    bg.init(&factors[prune_g1], &factors[prune_g2], verbose)
    bg.init_zigzag(1) // 1 => save subgraphs into bg.subgraph_id
    bg.compute_cores()
    bg.prune_1core(weight)
}

// bugbug fix or remove this fn altogether
`Void' FixedEffects::_expand_1core(`Variables' y)
{
    y = bg.expand_1core(y)
}

`Void' FixedEffects::save_touse(`Varname' touse, | `Boolean' replace)
{
    `Integer'               idx
    `Vector'                mask

    if (verbose > 0) printf("\n{txt} ## Saving e(sample)\n")
    mask = J(st_nobs(), 1, 0)
    mask[sample] = J(rows(sample), 1, 1)

    if (args()<2 | replace==.) {
        // Generate
        idx = st_addvar("byte", touse)
        st_store(., idx, mask)
    }
    else {
        // Replace
        st_store(., touse, mask)
    }
}

`Void' FixedEffects::save_variable(`Varname' varname,
                                   `Variables' data,
                                 | `Varlist' varlabel)
{
    `RowVector'               idx
    `Integer'               i
    `Vector'                mask
    idx = st_addvar("double", tokens(varname))
    st_store(sample, idx, data)
    if (args()>=3 & varlabel!="") {
        for (i=1; i<=cols(data); i++) {
            st_varlabel(idx[i], varlabel[i])
        }
    }

}

`Void' FixedEffects::post_footnote()
{
    `Matrix'                table
    `StringVector'          rowstripe
    `StringRowVector'       colstripe
    `String'                text

    assert(!missing(G))
    st_numscalar("e(N_hdfe)", G)
    st_numscalar("e(N_hdfe_extended)", G_extended)
    st_numscalar("e(df_a)", df_a)
    st_numscalar("e(df_a_initial)", df_a_initial)
    st_numscalar("e(df_a_redundant)", df_a_redundant)
    st_numscalar("e(df_a_nested)", df_a_nested)

    st_global("e(absvars)", invtokens(absvars))
    text = invtokens(extended_absvars)
    text = subinstr(text, "1.", "")
    st_global("e(extended_absvars)", text)

    // Absorbed degrees-of-freedom table
    table = (doflist_K \ doflist_M \ (doflist_K-doflist_M) \ !doflist_M_is_exact \ doflist_M_is_nested)'
    rowstripe = extended_absvars'
    rowstripe = J(rows(table), 1, "") , extended_absvars' // add equation col
    colstripe = "Categories" \ "Redundant" \ "Num Coefs" \ "Exact?" \ "Nested?" // colstripe cannot have dots on Stata 12 or earlier
    colstripe = J(cols(table), 1, "") , colstripe // add equation col
    st_matrix("e(dof_table)", table)
    st_matrixrowstripe("e(dof_table)", rowstripe)
    st_matrixcolstripe("e(dof_table)", colstripe)

    st_numscalar("e(ic)", iteration_count)
    st_numscalar("e(drop_singletons)", drop_singletons)
    st_numscalar("e(num_singletons)", num_singletons)
}

`Void' FixedEffects::post(`Options' options)
{
    `String'        text
    `Integer'       i

    post_footnote()

    // ---- constants -------------------------------------------------------

    st_global("e(predict)", "reghdfe_p")
    st_global("e(estat_cmd)", "reghdfe_estat")
    st_global("e(footnote)", "reghdfe_footnote")
    st_global("e(marginsok)", "")
    st_numscalar("e(df_m)", df_m)

    assert(title != "")
    text = sprintf("HDFE %s", title)
    st_global("e(title)", text)

    text = sprintf("Absorbing %g HDFE %s", G, plural(G, "group"))
    st_global("e(title2)", text)

    st_global("e(model)", model)
    st_global("e(cmdline)", cmdline)

    st_numscalar("e(tss)", tss)
    st_numscalar("e(tss_within)", tss_within)
    st_numscalar("e(rss)", rss)
    st_numscalar("e(mss)", tss - rss)
    st_numscalar("e(rmse)", rmse)
    st_numscalar("e(F)", F)

    st_numscalar("e(ll)", ll)
    st_numscalar("e(ll_0)", ll_0)

    st_numscalar("e(r2)", r2)
    st_numscalar("e(r2_within)", r2_within)
    st_numscalar("e(r2_a)", r2_a)
    st_numscalar("e(r2_a_within)", r2_a_within)

    if (!missing(N_clust)) {
        st_numscalar("e(N_clust)", N_clust)
        for (i=1; i<=num_clusters; i++) {
            text = sprintf("e(N_clust%g)", i)
            st_numscalar(text, N_clust_list[i])
        }
        text = "Statistics robust to heteroskedasticity"
        st_global("e(title3)", text)
    }

    if (!missing(sumweights)) st_numscalar("e(sumweights)", sumweights)

    // ---- .options properties ---------------------------------------------

    st_global("e(depvar)", depvar)
    st_global("e(indepvars)", indepvars)
    st_global("e(endogvars)", endogvars)
    st_global("e(instruments)", instruments)

    if (!missing(N_clust)) {
        st_numscalar("e(N_clustervars)", num_clusters)
        st_global("e(clustvar)", invtokens(clustervars))
        for (i=1; i<=num_clusters; i++) {
            text = sprintf("e(clustvar%g)", i)
            st_global(text, clustervars[i])
        }
    }

    if (residuals != "") {
        st_global("e(resid)", residuals)
    }

    // Stata uses e(vcetype) for the SE column headers
    // In the default option, leave it empty.
    // In the cluster and robust options, set it as "Robust"
    text = strproper(vcetype)
    if (text=="Cluster") text = "Robust"
    if (text=="Unadjusted") text = ""
    assert(anyof( ("", "Robust", "Jackknife", "Bootstrap") , text))
    if (text!="") st_global("e(vcetype)", text)

    text = vcetype
    if (text=="unadjusted") text = "ols"
    st_global("e(vce)", text)

    // Weights
    if (weight_type != "") {
        st_global("e(wexp)", "= " + weight_var)
        st_global("e(wtype)", weight_type)
    }
}

end