*! version 1.0 13/08/2014

program define katego
	version 10.0
	args varname newvar instruction

* General error verification
	local glue = 4
	while "``glue''" != "" {
		local instruction `instruction' ``glue''
		local glue = `glue'+1
		}
	if "`instruction'" == "" {
		di as error "Too few arguments, please see help file for details and examples"
		exit 197
		}

	confirm variable `varname'
	confirm numeric variable `varname'
	confirm new variable `newvar'
	qui summ `varname'
	local minv = r(min)
	local maxv = r(max)
	if r(N) == 0 {
		di as error "`varname' has only missing values"
		exit 459
		}

* Checking instruction and extracting info
	local instruction = itrim(trim("`instruction'")) // I need to verify if this line is actually necessary
	local tkns = wordcount("`instruction'") // tkns is the number of tokens in instruction
	if mod(`tkns',2) == 1 { // an odd number of tokens indicates an incomplete interval
		di as error "Incomplete instruction, please check if intervals are complete and that intervals are separated with spaces"
		exit 459
		}

	if regexm("`instruction'","min max") == 1 {
	di as error "Interval [min max] will create a single category"
	exit
	}

	tokenize `instruction'
	local tmu = `tkns' - 1

	foreach toke of numlist 1(2)`tmu' { // toke is the number of each token in instruction
		if regexm("``toke''",`"^(\[|\()(min|[0-9]+|[0-9]*.[0-9]+|-[0-9]+|-[0-9]*.[0-9]+)$"') == 1 { // checks if odd token has an aperture sequence: symbol+number
			local s`toke' = regexs(1) // saves the symbol in a macro
			if regexs(2) == "min" local n`toke' = `minv' // saves the minimum value in a macro when min is specified
			else local n`toke' = real(regexs(2)) // saves the number in a macro
			}
		else {
			di as error "Instruction was incorrectly specified in ``toke'', please see help for details and examples"
			exit 197
			}
		}

	foreach toke of numlist 2(2)`tkns' { // toke is the number of each token in instruction
		if regexm("``toke''",`"^(max|[0-9]+|[0-9]*.[0-9]+|-[0-9]+|-[0-9]*.[0-9]+)(\]|\))$"') == 1 { // checks if even token has a closure sequecence: number+symbol
			local s`toke' = regexs(2) // saves the symbol in a macro
			if regexs(1) == "max" local n`toke' = `maxv' // saves the maximum value in a macro when max is specified
			else local n`toke' = real(regexs(1)) // saves the number in a macro
			}
		else {
			di as error "Instruction was incorrectly specified in ``toke'', please see help for details and examples"
			exit 197
			}
		}

* Checking gaps and overlaps
	local tmd = `tkns'-2
	if `tkns' > 2 {
		foreach cnt of numlist 2(2)`tmd' {
			local masuno = `cnt' + 1
			local menosuno = `cnt' - 1
			local masdos = `cnt' + 2
			if `n`cnt'' > `n`masuno'' {
				di as error "Overlapping intervals `s`menosuno''`n`menosuno'' `n`cnt''`s`cnt'' `s`masuno''`n`masuno'' `n`masdos''`s`masdos''"
				exit 459
				}
			if `n`cnt'' < `n`masuno'' {
				di as error "Gap detected between intervals `s`menosuno''`n`menosuno'' `n`cnt''`s`cnt'' `s`masuno''`n`masuno'' `n`masdos''`s`masdos''"
				exit 459
				}
			if `n`cnt'' == `n`masuno'' & "`s`cnt''" == "]" & "`s`masuno''" == "[" {
				di as error "Overlapping intervals `s`menosuno''`n`menosuno'' `n`cnt''`s`cnt'' `s`masuno''`n`masuno'' `n`masdos''`s`masdos''"
				exit 459
				}
			if `n`cnt'' == `n`masuno'' & "`s`cnt''" == ")" & "`s`masuno''" == "(" {
				di as error "Gap detected between intervals `s`menosuno''`n`menosuno'' `n`cnt''`s`cnt'' `s`masuno''`n`masuno'' `n`masdos''`s`masdos''"
				exit 459
				}
			}
		}

* Checking inverse order
	foreach cnt of numlist 1(2)`tmu' {
			local masuno = `cnt' + 1
			if `n`cnt'' > `n`masuno'' {
				di as error "Inverse order in interval `s`cnt''`n`cnt'' `n`masuno''`s`masuno''"
				exit 459
				}
			if `n`cnt'' == `n`masuno'' & "`s`cnt''" == "(" & "`s`masuno''" == ")" {
				di as error "Interval `s`cnt''`n`cnt'' `n`masuno''`s`masuno'' causes an overlap"
				exit 459
				}
			if `n`cnt'' == `n`masuno'' & "`s`cnt''" == "[" & "`s`masuno''" == ")" {
				di as error "Interval `s`cnt''`n`cnt'' `n`masuno''`s`masuno'' is empty"
				exit 459
				}
			if `n`cnt'' == `n`masuno'' & "`s`cnt''" == "(" & "`s`masuno''" == "]" {
				di as error "Interval `s`cnt''`n`cnt'' `n`masuno''`s`masuno'' is empty"
				exit 459
				}
			}

* Determining number of categories
	if regexm("`instruction'","min") == 1 & regexm("`instruction'","max") == 1 local noc = `tkns'/2
	if regexm("`instruction'","min") == 1 & regexm("`instruction'","max") == 0 local noc = (`tkns'/2)+1
	if regexm("`instruction'","min") == 0 & regexm("`instruction'","max") == 1 local noc = (`tkns'/2)+1
	if regexm("`instruction'","min") == 0 & regexm("`instruction'","max") == 0 local noc = (`tkns'/2)+2

* Determining first and last position
	if regexm("`instruction'","min") == 1 local fir = 2
	else local fir = 1
	if regexm("`instruction'","max") == 1 local las = `tmu'
	else local las = `tkns'

* Creating the variable and categories
	qui generate `newvar' = .
	label var `newvar' "Categories of `varname'"

	* first category
	if "`s`fir''" == "(" | "`s`fir''" == "]" local symb "<="
	if "`s`fir''" == "[" | "`s`fir''" == ")" local symb "<"
	qui replace `newvar' = 0 if `varname' `symb' `n`fir''
	label define `newvar' 0 "`symb' `n`fir''"

	* last category
	if "`s`las''" == "(" | "`s`las''" == "]" local symb ">"
	if "`s`las''" == "[" | "`s`las''" == ")" local symb ">="
	qui replace `newvar' = `noc'-1 if `varname' `symb' `n`las''
	local cmu = `noc'-1
	label define `newvar' `cmu' "`symb' `n`las''", add

	* remaining categories in the middle
	if `noc' > 2 {
	local cim = `noc' - 2
	if `fir' == 2 local fir = 3
	local x1 = `fir'
	local x2 = `fir'+1
	foreach catval of numlist 1/`cim' {
		if "`s`x1''" == "(" local sya = ">"
		if "`s`x1''" == "[" local sya = ">="
		if "`s`x2''" == ")" local syb = "<"
		if "`s`x2''" == "]" local syb = "<="
		qui replace `newvar' = `catval' if `varname' `sya' `n`x1'' & `varname' `syb' `n`x2''
		label define `newvar' `catval' "`s`x1''`n`x1'' `n`x2''`s`x2''", add
		local x1 = `x1'+2
		local x2 = `x2'+2
		}
	}
	qui replace `newvar' = . if `varname' == .
	label values `newvar' `newvar'
end