/*-------------------------------------------------------------------------------
  metacollapse.ado: collapse dataset to its meta-information

    Copyright (C) 2011-2015  Daniel Bela (daniel.bela@lifbi.de)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

-------------------------------------------------------------------------------*/
*! metacollapse.ado: collapse dataset to its meta-information
*! Daniel Bela (daniel.bela@lifbi.de), Leibniz Institute for Educational Trajectories (LIfBi), Germany
*" version 1.11 27 July 2015 - do not abort if a single value label is longer than 244 characters (Stata's old string variable length limit)
*! version 1.10 21 July 2015 - only fix german umlauts in Stata versions older than 14
*! version 1.9 20 July 2012 - improved internal macro names and tempfile handling
*! version 1.8 01 March 2012 - saving outfile with -esave-
*! version 1.7 02 February 2012 - added support for Stata tempfiles (.tmp) as using datasets; small bugfixes
*! version 1.6 18 January 2012 - removed surplus temporary variable when using option `outfile'; small bugfixes
*! version 1.5 17 January 2012 - fixed omitting values when using `nofrequencies'; fixed option `full'
*! version 1.4 12 January 2012 - replaced option `allfrequencies' with `full'
*! version 1.3 13 October 2011 - speedup by using -contract-, bugfix to correctly handle special characters in variable names, option `noomitline' introduced
*! version 1.2 22 June 2011 - various bugfixes and new `round' option introduced; replaced `levelsof' with quicker alternatives
*! version 1.1 25 May 2011 - major overhaul to make the procedure more robust
*! version 1.0 07 April 2011 - initial release
program define metacollapse, nclass
	version 11.1
	syntax [using/] , [OUTFILEname(string) FORmat(passthru) MISSing DROPstrings THREshold(integer 20) PATHinformation NOFREquencies NOLABels ALLLABels FULL ROund(real 0) NOOMITline HUMANreadable CLEAR REPLACE VERBOSE]
	local umlautnum 0
	// confirm that only one of the options `nolabels' and `alllabels' is specified
	if "`alllabels'"!="" & "`nolabels'"!="" {
		noisily: display as error `"Error: you contradictorily specified both options {it:alllabels} and {it:nolabels}. Aborting."'
		error 198
	}
	// confirm that only one of the options `threshold' and `full' is specified
	if "`full'"!="" & "`threshold'"!="20" {
		noisily: display as error `"Warning: you contradictorily specified both options {it:full} and {it:threshold}. Option {it:threshold} ignored."'
	}
	// confirm that only one of the options `noomitline' and `full' is specified
	if "`full'"=="full" & "`noomitline'"=="noomitline" {
		noisily: display as error `"Warning: you contradictorily specified both options {it:full} and {it:noomitline}. Option {it:noomitline} ignored."'
	}
	// confirm that only one of the options `threshold' and `nofrequencies' is specified
	if "`nofrequencies'"!="" & "`threshold'"!="20" {
		noisily: display as error `"Warning: you contradictorily specified both options {it:nofrequencies} and {it:threshold}. Option {it:threshold} ignored."'
	}
	// preparations if output to file instead of working dataset requested
	if `"`outfilename'"'!="" {
		// save snapshot
		quietly: snapshot save
		local snapshotnum `r(snapshot)'
	}
	else {
		// verify current dataset to be saved before changing it, and trigger error if not
		if `c(changed)'!=0 & "`clear'"!="clear" error 4
		// drop warning message if `format' specified, but no `outfilename'
		if `"`format'"'!="" noisily: display as error `"Warning: you specified the {it:format} {bf:`format'},"'_newline`"but no filename to save to. Option {it:format} ignored."'
		// drop warning message if `replace' specified, but no `outfilename'
		if `"`format'"'!="" noisily: display as error `"Warning: you specified the {it:replace},"'_newline`"but no filename to save to. Option {it:replace} ignored."'
	}

	// open using file if requested
	if `"`using'"'!="" {
		// verify `using' to be a valid Stata dataset file
		if regexm(`"`using'"',`"^.+\.dta$"') {
			local strippedusing=regexr(`"`using'"',"\.dta$","")
		}
		else if regexm(`"`using'"',`"^.+\.tmp$"') {
			local strippedusing=regexr(`"`using'"',"\.tmp$","")
		}
		else {
			local strippedusing `"`using'"'
			local using `"`using'.dta"' // add ".dta" if no file extension given
		}
		confirm file `"`using'"'
		if "`verbose'"=="verbose" noisily: display as text `"Info: verification check pass - {it:`using'} found and readable."'
		quietly: use `"`using'"', clear
	}

	// save filename for latter use
	capture: extractfilename
	if _rc==0 {
		local name `r(filename)'
		local filedir `r(path)'
		local extension `r(extension)'
		local filename `name'.`extension'
	}
	else {
		local filename "<file not saved>"
		local filedir "<not applicable>"
	}


	// fetch and save the following value and variable meta information:
	/*********************************************************************************************************

	`var'_isstring				0 if variable is numeric, 1 if it is string

	for numeric variables:
		`var'_max				maximum of the variable
		`var'_min				minimum of the variable
		`var'_mean				mean of the variable
		`var'_stddev			standard deviation of the variable
		`var'_lq				first quartile of the variable
		`var'_median			median of the variable
		`var'_lq				third quartile of the variable
		`var'_desc				variable label
		`var'_label				value label container currently assigned to the variable
		`var'_unique			number of levels of the variable
		`var'_valid				number of nonmissing cases of the variable
		`var'_total				sum of missing and nonmissing cases of the variable
		`var'_invalid			number of missing cases of the variable

	for string variables:
		`var'_unique				number of levels of the variable
		`var'_valid					number of nonmissing cases of the variable
		`var'_total					sum of missing and nonmissing cases of the variable
		`var'_invalid				number of missing cases of the variable
	*********************************************************************************************************/

	if "`dropstrings'"=="dropstrings" {
		quietly: ds, has(type string)
		if !missing(`"`r(varlist)'"') quietly: drop `r(varlist)'
	}
	unab allvars: _all
	local allvarcount: word count `allvars'

	// convert special characters in variable names if needed
	if (`c(stata_version)'<14) {
		foreach var of local allvars {
			if regexm(`"`var'"',"ß")==1 | regexm(`"`var'"',"ä")==1 | regexm(`"`var'"',"ö")==1 | regexm(`"`var'"',"ü")==1 {
				local umlautorig`++umlautnum' `var'
				local umlautnew`umlautnum': subinstr local var "ß" "sz", all
				local umlautnew`umlautnum': subinstr local umlautnew`umlautnum' "ä" "ae", all
				local umlautnew`umlautnum': subinstr local umlautnew`umlautnum' "ü" "ue", all
				local umlautnew`umlautnum': subinstr local umlautnew`umlautnum' "ö" "oe", all
				local umlautnew`umlautnum': subinstr local umlautnew`umlautnum' "Ä" "AE", all
				local umlautnew`umlautnum': subinstr local umlautnew`umlautnum' "Ü" "UE", all
				local umlautnew`umlautnum': subinstr local umlautnew`umlautnum' "Ö" "OE", all
			}
		}
		forvalues num=1/`umlautnum' {
			local allvars: subinstr local allvars "`umlautorig`num''" "__`umlautnew`num''", all
			quietly: rename `umlautorig`num'' __`umlautnew`num''
		}
	}

	// start procedure
	forvalues var=1/`allvarcount'{
		local `var'_name: word `var' of `allvars'
		if "`verbose'"=="verbose" noisily: display as text `"Info: fetching data for {it:``var'_name'}."'
		// check if variable is string
		if strmatch("`: type ``var'_name''","str*") local `var'_isstring 1
		else local `var'_isstring 0

		// fetch general information
		quietly: count if missing(``var'_name')
		local `var'_invalid `r(N)'
		quietly: count if !missing(``var'_name')
		local `var'_valid `r(N)'
		local `var'_desc: variable label ``var'_name'

		if ``var'_isstring'==0 {
			// fetch further information if numeric
			quietly: summarize ``var'_name', detail
			local `var'_max `r(max)'
			local `var'_min	`r(min)'
			local `var'_mean `r(mean)'
			local `var'_stddev `r(sd)'
			local `var'_lq `r(p25)'
			local `var'_median `r(p50)'
			local `var'_uq `r(p75)'
			local `var'_label: value label ``var'_name'
		}

		// fill in empty general information if not available
		if "``var'_max'"=="" local `var'_max .		// replace with missing if still empty (in case of no observations)
		if "``var'_min'"=="" local `var'_min .		// replace with missing if still empty (in case of no observations)
		if "``var'_mean'"=="" local `var'_mean .		// replace with missing if still empty (in case of no observations)
		if "``var'_stddev'"=="" local `var'_stddev .		// replace with missing if still empty (in case of no observations)
		if "``var'_lq'"=="" local `var'_lq .		// replace with missing if still empty (in case of no observations)
		if "``var'_median'"=="" local `var'_median .		// replace with missing if still empty (in case of no observations)
		if "``var'_uq'"=="" local `var'_uq .		// replace with missing if still empty (in case of no observations)

		// generate frequency dataset for each variable
		preserve
		quietly: contract ``var'_name', freq(frequency)
		quietly: count
		local `var'_unique `r(N)'
		if "`missing'"!="missing" quietly: drop if missing(``var'_name')
		if ``var'_isstring'==0 quietly: generate labelname="``var'_label'"
		quietly: rename ``var'_name' value
		sort value
		quietly: generate varname="``var'_name'"
		if  ``var'_unique'>0 {
			if ``var'_isstring'==1 quietly: rename value valuelabel
			tempname freqs`var'
			tempfile `freqs`var''
			quietly: save ``freqs`var''', replace
		}
		restore
		if "`verbose'"=="verbose" noisily: display as result`"Info: ...done."'
	}

	// generate complete frequency dataset
	if "`nofrequencies'"!="nofrequencies" {
		if "`verbose'"=="verbose" noisily: display as text `"Info: generating temporary frequency dataset."'
		preserve
		clear
		quietly: generate str244 varname=""
		label variable varname "Variable name"
		quietly: generate str244 labelname=""
		label variable labelname "Associated value label"
		quietly: generate str244 valuelabel=""
		label variable valuelabel "Distinct value label"
		quietly: generate double value=.
		label variable value "Variable's values"
		quietly: generate double frequency=.
		label variable frequency "Distinct frequency of value"
		forvalues var=1/`allvarcount' {
			quietly: append using "``freqs`var'''", nolabel nonotes
		}

		// save as temporary dataset
		tempname freqinformation
		tempfile `freqinformation'
		quietly: compress
		quietly: save ``freqinformation'', replace
		if "`verbose'"=="verbose" noisily: display as result`"Info: ...done."'
		restore
	}

	// generate label dataset
	if "`nolabels'"!="nolabels" {
		if "`verbose'"=="verbose" noisily: display as text `"Info: generating temporary label dataset."'
		quietly: label dir
		if `"`r(names)'"'!="" {
			preserve
			uselabel _all, clear var
			// quick save label <-> variable associations
			local allcontainers `r(__labnames__)'
			local containercount: word count `allcontainers'
			forvalues contnum=1/`containercount' {
				local contname`contnum': word `contnum' of `allcontainers'
				local contvars`contnum' `r(`contname`contnum'')'
				local contvarcount`contnum': word count `contvars`contnum''
				forvalues contvarnum=1/`contvarcount`contnum'' {
					local contvar`contnum'_`contvarnum': word `contvarnum' of `contvars`contnum''
				}

			}
			forvalues contnum=1/`containercount' {
				quietly: count if lname=="`contname`contnum''"
				local contassociations`contnum'=`r(N)'
			}
			rename lname labelname
			rename label valuelabel
			label variable value "Variable's values"
			label variable valuelabel "Distinct value label"
			quietly: keep labelname value valuelabel
			quietly: generate str244 varname=""
			label variable varname "Variable name"
			// remove missing values if not requested
			if `"`missing'"'!="missing" {
				foreach missval in `c(alpha)' {
					quietly: drop if value==.`missval'
				}
			}
			// drop entries not needed any more
			if `"`alllabels'"'!="alllabels" & `"`nofrequencies'"'!=`"nofrequencies"' {
				quietly: merge 1:m labelname value using ``freqinformation'', keepusing(frequency) keep(match) nogenerate noreport
				quietly: drop frequency
				quietly: duplicates drop
			}
			// enter variable name, duplicate entries before doing so if necessary
			tempvar expansion
			forvalues contnum=1/`containercount' {
				forvalues contvarnum=1/`contvarcount`contnum'' {
					if `contvarnum'<`contvarcount`contnum'' {
						quietly: expand 2 if labelname=="`contname`contnum''" & varname=="", generate(`expansion')
						quietly: replace varname="`contvar`contnum'_`contvarnum''" if `expansion'==1
						quietly: drop `expansion'
					}
					else if `contvarnum'==`contvarcount`contnum'' quietly: replace varname="`contvar`contnum'_`contvarnum''" if labelname=="`contname`contnum''" & varname==""
					else error 691
				}
			}
			// save as temporary dataset
			tempname labelinformation
			tempfile `labelinformation'
			quietly: compress
			quietly: save ``labelinformation'', replace
			if "`verbose'"=="verbose" noisily: display as result`"Info: ...done."'
			restore
		}
		else local nolabels nolabels
	}

	// generate raw collapsed dataset
	if "`verbose'"=="verbose" noisily: display as text `"Info: generating final dataset."'
	quietly: describe, replace clear
	rename isnumeric vartype
	label define VARTYPE 0 "string" 1 "numeric"
	label values vartype VARTYPE
	label variable vartype "Type of variable (0=string/1=numeric)"
	quietly: drop type format
	label variable position "Position of variable in dataset"
	quietly: rename varlab varlabel
	recast str244 varlabel
	label variable varlabel "Variable label"
	quietly: rename vallab labelname
	recast str244 labelname
	label variable labelname "Associated value label"
	quietly: rename name varname
	recast str244 varlabel
	label variable varname "Variable name"
	*foreach var of local allvars {
	forvalues var=1/`allvarcount' {
		preserve
		quietly: keep if varname=="``var'_name'"
		local `var'_position=position
		restore
	}
	quietly: generate double minimum=.
	label variable minimum "Lowest valid value"
	quietly: generate double maximum=.
	label variable maximum "Highest valid value"
	quietly: generate double mean=.
	label variable mean "Mean of variable"
	quietly: generate double stddev=.
	label variable stddev "Standard deviance of variable"
	quietly: generate double quartile1=.
	label variable quartile1 "1st quartile of variable"
	quietly: generate double median=.
	label variable median "Median of variable"
	quietly: generate double quartile3=.
	label variable quartile3 "3rd quartile of variable"
	quietly: generate double valid=.
	label variable valid "Number of nonmissing cases for the variable"
	quietly: generate double invalid=.
	label variable invalid "Number of missing cases for the variable"
	quietly: generate double unique=.
	label variable unique "Number of unique values for the variable"
	quietly: generate str244 dataset=""
	label variable dataset "Dataset filename"
	if "`pathinformation'"=="pathinformation" {
		quietly: generate str244 filepath=""
		label variable filepath "Dataset filepath"
	}
	if "`verbose'"=="verbose" noisily: display as result`"Info: ...done."'

	// combine collapsed dataset with frequency and meta information
	if "`nofrequencies'"!="nofrequencies" {
		if "`verbose'"=="verbose" noisily: display as text `"Info: merging frequency information."'
		quietly: merge 1:m varname using ``freqinformation'', keepusing(varname value frequency valuelabel) assert(match master) noreport nogenerate
		if "`verbose'"=="verbose" noisily: display as result`"Info: ...done."'
	}
	if "`nolabels'"!="nolabels" {
		if "`verbose'"=="verbose" noisily: display as text `"Info: merging label information."'
		if "`nofrequencies'"!="nofrequencies" {
			if "`alllabels'"=="alllabels" & "`full'"!="full" quietly: merge m:1 varname labelname value using ``labelinformation'', keepusing(varname labelname value valuelabel) update assert(match master using match_update) noreport nogenerate
			else if "`full'"!="full" quietly: merge m:1 varname labelname value using ``labelinformation'', keepusing(varname labelname value valuelabel) update assert(match master using match_update) keep(match master match_update) noreport nogenerate
			else quietly: merge m:1 varname labelname value using ``labelinformation'', keepusing(varname labelname value valuelabel) update assert(match master using match_update) noreport nogenerate
		}
		else {
			quietly: generate value=.
			label variable value "Variable's values"
			quietly: merge m:1 varname labelname value using ``labelinformation'', keepusing(labelname value valuelabel) assert(match master using) noreport nogenerate
		}
		if "`verbose'"=="verbose" noisily: display as result`"Info: ...done."'
	}

	// order dataset
	order dataset position vartype varname varlabel labelname valid invalid unique minimum maximum mean stddev quartile1 median quartile3
	if "`pathinformation'"=="pathinformation" order filepath, before(dataset)
	if "`nolabels'"!="nolabels" | "`nofrequencies'"!="nofrequencies" order valuelabel value, after(labelname)
	if "`nolabels'"!="nolabels" | "`nofrequencies'"!="nofrequencies" order valuelabel, before(value)
	if "`nofrequencies'"!="nofrequencies" {
		order frequency, after(value)
		quietly: replace frequency=0 if missing(frequency)
	}

	// add information to final dataset
	if "`pathinformation'"=="pathinformation" {
		quietly: replace filepath=`"`filedir'"'
	}
	quietly: replace dataset=`"`filename'"'
	*foreach var of local allvars {
	forvalues var=1/`allvarcount' {
		// insert general information
		if "`verbose'"=="verbose" noisily: display as text `"Info: inserting general information for {it:``var'_name'}."'
		quietly: replace varlabel=`"``var'_desc'"' if varname=="``var'_name'"
		quietly: replace position=``var'_position' if varname=="``var'_name'"
		quietly: replace valid=``var'_valid' if varname=="``var'_name'"
		quietly: replace invalid=``var'_invalid' if varname=="``var'_name'"
		quietly: replace unique=``var'_unique' if varname=="``var'_name'"

		// insert statistics if numeric
		if ``var'_isstring'==0 {
			quietly: replace vartype=1 if varname=="``var'_name'"
			quietly: replace minimum=``var'_min' if varname=="``var'_name'"
			quietly: replace maximum=``var'_max' if varname=="``var'_name'"
			quietly: replace mean=``var'_mean' if varname=="``var'_name'"
			quietly: replace stddev=``var'_stddev' if varname=="``var'_name'"
			quietly: replace quartile1=``var'_lq' if varname=="``var'_name'"
			quietly: replace median=``var'_median' if varname=="``var'_name'"
			quietly: replace quartile3=``var'_uq' if varname=="``var'_name'"
		}
		// insert vartype if string
		else {
			quietly: replace vartype=0 if varname=="``var'_name'"
		}
		if "`verbose'"=="verbose" noisily: display as result`"Info: ...done."'
	}

	// generate valueposition
	if `"`nolabels'"'!="nolabels" | `"`nofrequencies'"'!="nofrequencies" {
		quietly: generate valueposition=.
		label variable valueposition "Position of value in variable"
		quietly: bysort position (value): replace valueposition=_n if vartype==1
		quietly: bysort position (valuelabel): replace valueposition=_n if vartype==0
	}

	// sort dataset
	if `"`nolabels'"'=="nolabels" & `"`nofrequencies'"'=="nofrequencies" sort position
	else sort position valueposition

	// omit information
	if (!(`"`nolabels'"'=="nolabels" & `"`nofrequencies'"'=="nofrequencies")) & "`full'"!="full" {
		tempvar omitline omitfreq
		quietly: generate `omitline'=.
		*foreach var of local allvars {
		forvalues var=1/`allvarcount' {
			local omittedvalues ""
			local omittedfreqs ""
			quietly: count if varname==`"``var'_name'"'
			local varmax `r(N)'
			if `varmax'>`threshold' {
				quietly: count if varname==`"``var'_name'"' & (valueposition>`threshold'/2 & valueposition<=`varmax'-(`threshold'/2))
				local omittedvalues `r(N)'
				if `"`nofrequencies'"'!="nofrequencies" {
					quietly: egen `omitfreq'=total(frequency) if varname==`"`var'"' & (valueposition>`threshold'/2 & valueposition<=`varmax'-(`threshold'/2))
					preserve
					quietly: keep if varname==`"`var'"' & (valueposition>`threshold'/2 & valueposition<=`varmax'-(`threshold'/2))
					local omittedfreqs=`omitfreq'
					restore
					quietly: drop `omitfreq'
				}
				quietly: drop if varname==`"``var'_name'"' & (valueposition>`threshold'/2 & valueposition<=`varmax'-(`threshold'/2))
				// insert "omited X values" if needed
				if `"`noomitline'"'!="noomitline" {
					quietly: expand 2 if varname==`"``var'_name'"' & valueposition==`threshold'/2
					quietly: replace `omitline'=1 in l
					quietly: replace labelname="<`omittedvalues' values omitted>" in l
					quietly: replace valuelabel="" in l
					if `"`nofrequencies'"'!="nofrequencies" quietly: replace frequency=`omittedfreqs' in l
					quietly: replace valueposition=valueposition+0.1 in l
				}
			}
		}
		sort position valueposition
		if `"`noomitline'"'!="noomitline" {
			quietly: replace value=. if `omitline'==1
			quietly: replace valueposition=. if `omitline'==1
		}
		drop `omitline'
	}
	if `"`nolabels'"'!="nolabels" | `"`nofrequencies'"'!="nofrequencies" quietly: drop valueposition

	// insert "no label" tags
	if `"`nolabels'"'!="nolabels" {
		quietly: replace labelname="<no value label>" if labelname=="" & vartype==1
		capture: confirm variable valuelabel
		if `c(rc)'==0 {
			quietly: replace valuelabel=labelname if strmatch(labelname,"* values omitted>")
			quietly: replace valuelabel="<value not labeled>" if valuelabel=="" & labelname!="<no value label>"
			quietly: replace valuelabel="<no value label>" if valuelabel=="" & (labelname=="<no value label>" | strmatch(labelname,"* values omitted>"))
		}
		*foreach var of local allvars {
		forvalues var=1/`allvarcount' {
			if `"``var'_desc'"'=="" quietly: replace varlabel="<no variable label>" if varname=="``var'_name'"
		}
	}
	else {
		quietly: replace labelname="<no value label>" if labelname=="" & vartype==1
		capture: confirm variable valuelabel
		if `c(rc)'==0 {
			quietly: replace valuelabel=labelname if strmatch(labelname,"* values omitted>")
			quietly: replace valuelabel="<labels not requested>" if valuelabel=="" & labelname!="<no value label>"
			quietly: replace valuelabel="<no value label>" if valuelabel=="" & (labelname=="<no value label>" | strmatch(labelname,"* values omitted>"))
		}
		*foreach var of local allvars {
		forvalues var=1/`allvarcount' {
			if `"``var'_desc'"'=="" quietly: replace varlabel="<no variable label>" if varname=="``var'_name'"
		}
	}

	// round values if requested
	if `round'!=0 {
		if "`verbose'"=="verbose" noisily: display as text `"Info: rounding values in units of {it:`round'}."'
		foreach var of varlist minimum maximum mean stddev quartile1 median quartile3 {
			quietly: replace `var'=round(`var',`round')
		}
		if "`nofrequencies'"!="nofrequencies" & "`nolabels'"!="nolabels" quietly: replace value=round(value,`round')
	}
	label data `"metacollapse.ado: metacollapsed description of <`filename'>"'

	// reconvert special characters in variable names if needed
	if `umlautnum'>0 {
		forvalues num=1/`umlautnum'{
			quietly: replace varname="`umlautorig`num''" if varname=="__`umlautnew`num''"
		}
	}
	quietly: compress
	foreach var of varlist _all {
		if "`: type `var''"=="byte" format %8.0g `var'
		else if "`: type `var''"=="int" format %8.0g `var'
		else if "`: type `var''"=="long" format %12.0g `var'
		else if "`: type `var''"=="float" format %9.0g `var'
		else if "`: type `var''"=="double" format %10.0g `var'
		else {
			assert regexm("`: type `var''","^str([0-9][0-9]*|L?)$")==1
			local targetformat=regexs(1)
			if (`"`targetformat'"'=="L" | `targetformat'>100) local targetformat 100
			format %`targetformat's `var'
		}
	}
	// clean up for better readability if requested
	if "`humanreadable'"=="humanreadable" {
		if "`verbose'"=="verbose" noisily: display as text `"Info: deleting redundant information for better legibility."'
		tempvar position
		quietly: generate `position'=position
		foreach var in dataset varname varlabel labelname {
			tempvar `var'
			quietly: generate ``var''=`var'
			quietly: replace `var'="" if (`position'==`position'[_n-1] & ``var''==``var''[_n-1])
			quietly: drop ``var''
		}
		quietly: replace labelname="" if strmatch(labelname[_n-1],"* values omitted>")
		foreach var in position vartype minimum maximum mean stddev quartile1 median quartile3 valid invalid unique {
			quietly: replace `var'=. if `position'==`position'[_n-1]
		}
		if "`pathinformation'"=="pathinformation" quietly: replace filepath="" if `position'==`position'[_n-1]
		quietly: drop `position'
	}
	// save to file if requested
	if `"`outfilename'"'!="" {
		quietly: extractfilename `"`outfilename'"'
		local usext `r(extension)'
		if "`format'"=="format(sas)" | "`format'"=="" & "`usext'"=="xpt" quietly: esave `"`outfilename'"', `replace' `format' rename vallabfile(xpf)
		if inlist("`format'","format(ooxml)","format(excel)") | "`format'"=="" & inlist("`usext'","xlsx","xls") quietly: esave `"`outfilename'"', `replace' `format' firstrow(variables)
		else quietly: esave `"`outfilename'"', `replace' `format'
		quietly: snapshot restore `snapshotnum'
		quietly: snapshot erase `snapshotnum'
		if "`verbose'"=="verbose" noisily: display as text `"Info: working file restored."'
	}
end
//EOF