*! version 1.6 Juni 4, 2013 @ 21:43:14
*! Sumarize information on (person specific) sequences

* version 1.0 SJ contribution
* version 1.1 New Option: subsequence()
* version 1.2 Tempfiles in compount double quotes
* version 1.3 -keepranks- with -so- and -ss- resulted in error. This is fixed.
* version 1.4
* version 1.5 bug with some Window's -> fixed
* version 1.6 change string representation

program define sqtab, rclass
version 9
	syntax [varname(default=none)] [if] [in] ///
	  [, nosort ranks(numlist) so se gapinclude SUBSEQuence(string) * ]

	// Sq-Data
	if "`_dta[SQis]'" == "" {
		di as error "data not declared as SQ-data; use -sqset-"
		exit 9
	}

	// if/in
	if "`if'" != "" {
		tokenize "`if'", parse(" =+-*/^~!|&<>(),.")
		while "`1'" != "" {
			capture confirm variable `1'
			if !_rc {
				local iflist  "`iflist' `1'"
			}
			macro shift
		}
	}
	if "`iflist'" != "" CheckConstant `iflist', stop
	marksample touse
	if "`subsequence'" != "" quietly replace `touse' = 0 if !inrange(`_dta[SQtis]',`subsequence')


	// Options
	if "`varlist'" == "" local sort = cond("`sort'"=="nosort","","sort")
	else local sort = ""

	quietly {

		preserve

		// Drop Sequences with Gaps
		if "`gapinclude'" == "" {
			tempvar lcensor rcensor gap
			by `_dta[SQiis]' (`_dta[SQtis]'), sort: gen `lcensor' = sum(!mi(`_dta[SQis]'))
			by `_dta[SQiis]' (`_dta[SQtis]'): gen `rcensor' = sum(mi(`_dta[SQis]'))
			by `_dta[SQiis]' (`_dta[SQtis]'): ///
			  replace `rcensor' = ((_N-_n) == (`rcensor'[_N]-`rcensor'[_n])) & mi(`_dta[SQis]')
			by `_dta[SQiis]' (`_dta[SQtis]'): ///
			  gen `gap' = sum(mi(`_dta[SQis]') & `lcensor' & !`rcensor')
			by `_dta[SQiis]' (`_dta[SQtis]'): ///
			  replace `touse' = 0 if `gap'[_N]>0
		}
		keep if `touse'
		if _N == 0 {
			noi di as text "(No observations)"
			exit
		}

		if "`so'" == "so" {
			by `_dta[SQiis]' (`_dta[SQtis]'), sort: keep if `_dta[SQis]' ~= `_dta[SQis]'[_n-1]
			by `_dta[SQiis]' (`_dta[SQtis]'): replace `_dta[SQtis]' = _n
		}

		if "`se'" == "se" {
			by `_dta[SQiis]' `_dta[SQis]', sort: keep if _n == 1
			by `_dta[SQiis]' (`_dta[SQis]'): replace `_dta[SQtis]' = _n
		}

		// Option ranks
		if "`ranks'" != "" {
			tempfile original frequency
			save `"`original'"'
			tempvar freq rank select
			keep `_dta[SQiis]' `_dta[SQtis]' `_dta[SQis]' `touse'
			keep if `touse'
			reshape wide `_dta[SQis]', i(`_dta[SQiis]') j(`_dta[SQtis]')
			by `_dta[SQis]'*, sort: gen `freq' = _N * (-1)
			by `freq' `_dta[SQis]'*, sort: gen int `rank' = _n==1
			replace `rank' = sum(`rank')
			by `freq', sort: replace `rank' = `rank'[1]
			gen int `select' = 0
			foreach r of local ranks {
				replace `select' = 1 if `rank'  == `r'
			}
			keep if `select'
			replace `freq' = `freq' * -1
			sort `_dta[SQiis]'
			save `"`frequency'"'
			use `"`original'"', clear
			sort `_dta[SQiis]'
			merge `_dta[SQiis]' using `"`frequency'"'
			keep if _merge==3
			drop _merge

			count
			if r(N) == 0 {
				noisily di "{err} No observations for rank(s) `ranks'. Choose different ranks"
				exit 198
			}
		}

		// Create building blocks for string representation
		// -------------------------------------------------

		tempvar stringpiece episode

		by `_dta[SQiis]' (`_dta[SQtis]'), sort:  ///
		  gen byte `episode' = 1 if `_dta[SQis]' != `_dta[SQis]'[_n-1]

		by `_dta[SQiis]' (`_dta[SQtis]'), sort:  ///
		  replace `episode' = sum(`episode')

		by `_dta[SQiis]' `episode' (`_dta[SQtis]'), sort: ///
		  gen `stringpiece'  ///
		  = string(`_dta[SQis]')+ cond(_N>1,":" +string(_N),"")  ///
		  if _n==1

		// Reshape to Wide
		// ---------------

		keep `varlist' `iflist' `_dta[SQiis]' `_dta[SQtis]' `_dta[SQis]' `freq' `touse' `stringpiece'
		reshape wide `_dta[SQis]' `stringpiece', i(`_dta[SQiis]') j(`_dta[SQtis]')
		sort `_dta[SQis]'*


		// Generate Display-Variable
		// -------------------------

		tempvar pattern

		// Create string representation (from egen-concat)
		gen str1 `pattern' = ""
		foreach var of varlist `stringpiece'* {
			replace `pattern' = trim(`pattern') + " " + trim(`var')
		}
		replace `pattern' = trim(`pattern')

		// I want labels
		local label = ////
		  cond("`se'"=="" & "`so'"=="","Sequence-Pattern",  ///
		  cond("`se'"=="" & "`so'"!="","Sequence-Order", ///
		  cond("`se'"!="" & "`so'"=="","Sequence-Elements","")))
		label variable `pattern' "`label'"

	}

		// Output
		// ------

		local fweight = cond("`ranks'"=="","","")
		tab `pattern' `varlist' `fweight', `sort' `options'

		// Return
		// ------

		return scalar N = r(N)
		return scalar r = r(r)
		if "`varlist'" != "" return scalar c = r(c)
end


program CheckConstant, rclass
	syntax varlist(default=none) [, stop]
	sort `_dta[SQiis]'
	foreach var of local varlist {
		capture by `_dta[SQiis]': assert `var' == `var'[_n-1] if _n != 1
		if _rc & "`stop'" == "" {
			di as res "`var'" as text " is not constant over time; not used"
			local varlist: subinstr local varlist "`var'" "", word
		}
		if _rc & "`stop'" != "" {
			di as error "`var' is not constant over time"
			exit 9
		}
		if "`stop'" == "" {
			return local checked "`varlist'"
		}
	}
end