*! version 4.2 18nov2010 -- Ph. Van Kerm
*   -- update to Stata 11 syntax
* version 4.1 26aug2010 -- Ph. Van Kerm
*   -- add cdf variability bands
* version 4.0 10mar2010,13jul2010 -- Ph. Van Kerm
*   -- add cdf() option
* version 3.0 22sep2005 -- Ph. Van Kerm         (SJ3-2: st0037, SJ4-1: st0037_1, SJ?-?: st0037_2)
* version 2.0 5feb2003 -- Ph. Van Kerm         (SJ3-2: st0037, SJ4-1: st0037_1)
* version 1.0 6jan2003


* AKDENSITY is a wrapper to produce 2-stage
* adaptive kernel density estimates.
* Estimates are produced by repeated calls to
* AKDENSITY0. Most of the code is
* drawn from the official KDENSITY command.
* Works both with Stata 7 and Stata 8.
* Bug fix 2005-09: -gauss- option was
*  ineffective in Stata 8 and above
* Also option EPAN2 added (for alternative
*  Epanechnikov kernel)


 program define akdensity , rclass sortpreserve

 version 7.0

 if _caller()<8  {
   syntax varname(numeric) [if] [in] [fw aw] [, /*
		*/ Generate(string) N(integer 50) CDF(string) /*
		*/ Width(real 0.0) BWidth(real 0.0) noGRaph noDENSity /*
		*/ EPan GAUssian EPAN2 Kernel(string) noADAPtive /*
		*/ Symbol(string) Connect(string) /*
		*/ Title(string) AT(varname) NORmal STUdent(int 0) /*
		*/ STDBands(real 0.0) * ]
    }
  else {
    version 8.0
	syntax varname(numeric)			///
		[if] [in] [fw aw] [,		///
		Generate(string)		///
		CDF(string)				///
		AT(varname)			///
		N(integer 50)			///
		Width(real 0.0)			///
		BWidth(real 0.0)		///
		noADAPtive          ///
		STDBands(real 0.0)  ///
		noGRaph				///
		Kernel(string)			///
		EPanechnikov			///
		GAUssian			///
		EPAN2   		///
		NORmal				///
		STUdent(int 0)			///
		*				///
	]
	if `"`graph'"' != "" {
		_get_gropts , graphopts(`options')
		syntax varname(numeric)			///
			[if] [in] [fw aw] [,		///
			Generate(string)		///
			CDF(string)				///
			AT(varname)			///
			N(integer 50)			///
			Width(real 0.0)			///
			BWidth(real 0.0)		///
   		    noADAPtive          ///
		    STDBands(real 0.0)  ///
			noGRaph				///
			Kernel(string)			///
			EPanechnikov			///
			EPAN2			///
			GAUssian			///
		]
	  }
	_get_gropts , graphopts(`options')	///
		getallowed(stopts NORMOPTS plot addplot)
	local options `"`s(graphopts)'"'
	local normopts `"`s(normopts)'"'
	local stopts `"`s(stopts)'"'
	_check4gropts normopts, opt(`normopts')
	_check4gropts stopts, opt(`stopts')
	if `"`normopts'"' != "" {
		local normal normal
	}
	if `"`stopts'"' != "" & `student' < 1 {
		di as err "option student() is required by stopts() option"
		exit 198
	}
	local plot `"`s(plot)'"'
	local addplot `"`s(addplot)'"'
   // end of added for 8
   }


  version 7.0

	if "`at'"!="" & `n'!=50 {
		di in red "may not specify both the at() and n() options"
		exit 198
	}

    ** added 2011-11-18 (to update to Stata 11 -kernel()- option)
    ** copied from kdensity.ado with adjustment to allow only epan epan2 gauss
	local kernel_old  ///
				`epanechnikov'	///
				`gaussian'	///
				`epan2'
	local k : word count `kernel_old'
		if `"`kernel'"' == "" {
			if `k' > 1 {
				di as err "only one kernel may be specified"
				exit 198
			}
			if `k' == 0 {
				local kernel epanechnikov
			}
			else {
				local kernel `kernel_old'
			}
		}
		else {
			if `k' != 0 {
				di as err "kernel(): old syntax "    ///
					  "may not be combined with new syntax"
				exit 198
			}
			local k : word count `kernel'
			if `k' > 1 {
				di as err "only one kernel may be specified"
				exit 198
			}
			_get_kernel_name, kernel(`"`kernel'"')
			local kernel `s(kernel)'
			if `"`kernel'"' == "" {
				di as err "invalid kernel function"
				exit 198
			}
		}  // **end addition 2010-11-18


	local ix `"`varlist'"'
	local ixl: variable label `ix'
	if `"`ixl'"'=="" {
		local ixl "`ix'"
	}

    ** added 2011-11-18 (to update to Stata 11 -bwidth()- option)
    ** copied from kdensity.ado
	if `bwidth' != 0 {
		if `width' != 0 {
			di as err ///
			     "options width() and bwidth() may not be combined"
			exit 198
		}
		local width = `bwidth'
	}
	if `width' < 0 {
		di as err ///
		     "bandwidth must be positive"
		exit 198
	}
	**end addition 2010-11-18



	local gen `"`generate'"'

/* drop 2010-11-17 -- treated above
	local kflag = ( (`"`epan'"' != `""') + (`"`gauss'"' != `""') + (`"`epan2'"' != `""') )
	if `kflag' > 1 {
		di in red `"only one kernel may be specified"'
		exit 198
	}

    if `"`gauss'"'  != `""' {
       local kernel=`"Gaussian"'
       }
	else {
		if `"`epan2'"'  !=  `""'  {
       		local kernel=`"Alternative Epanechnikov"'
        }
        else {
       local kernel=`"Epanechnikov"'
        }
	}
*/

	marksample use
	qui count if `use'
	if r(N)==0 {
           error 2000
           }

	tokenize `gen'
	local wc : word count `gen'
	if `wc' {
		if `wc' == 1 {
			if `"`at'"' == `""' {
				error 198
			}
			confirm new var `1'
			local yl  `"`1'"'
			local xl `"`at'"'
			local nsave 1
		}
		else {
			if `wc' != 2 {
                    error 198
                    }
			confirm new var `1'
			confirm new var `2'
			local xl  `"`1'"'
			local yl  `"`2'"'
			local nsave 2
		}
	}
	else {
		local xl   `"X"'
		local yl   `"Density"'
		local nsave 0
	}

	if ("`cdf'" != "") {
		if ! (("`at'" != "") | (`wc' == 2) ) {
			di in red `"cdf() requires at() or generate(var1 var2) options be specified"'
			exit 198
		}
		confirm new var `cdf'
		loc cdfopt cdf(`cdf')
	}

	if (`stdbands' > 0.0) & (`wc' >= 0) {
		confirm new var `yl'_up
		confirm new var `yl'_lo
		if ("`cdf'" != "") {
			confirm new var `cdf'_up
			confirm new var `cdf'_lo
			}
		}


	tempvar d m
	qui gen double `m'=.

	if `"`at'"' != `""' {
		qui count if `at' != .
		local n = r(N)
		qui replace `m' = `at'
		local srtlst : sortedby
		tempvar obssrt
		gen `obssrt' = _n
		sort `m' `obssrt'
	}
	else {
		if `"`n'"'!= `""' {
			if `n' <= 1 {
                    local n = 50
                    }
			if `n' > _N {
				local n = _N
				noi di in gr `"(n() set to "' `n' `")"'
			}
		}
	}


	quietly summ `ix' [`weight'`exp'] if `use', detail
	local ixmean = r(mean)
	local ixsig = r(Var)
	local ixsd = r(sd)

	tempname wwidth
	scalar `wwidth' = `width'
	if `wwidth' <= 0.0 {
		scalar `wwidth' = min( sqrt(r(Var)), (r(p75)-r(p25))/1.349)
		scalar `wwidth' = 0.9*`wwidth'/(r(N)^.20)
	}

	tempname delta wid
	scalar `delta' = (r(max)-r(min)+2*`wwidth')/(`n'-1)
	scalar `wid'   = r(N) * `wwidth'

	if `"`at'"' == `""' {
		qui replace `m' = r(min)-`wwidth'+(_n-1)*`delta' in 1/`n'
	}

    if (`stdbands')>0 {
      loc nbands = `stdbands'
      loc nbands "stdbands(`nbands')"
      }

	tempvar tmpd tmplambda tmph
	if "`adaptive'"=="" {
	  ** 2010-11-18: replace gauss epan epan2 by kernel
		di as text "Two-stage adaptive kernel density estimation"
		di as text "Step 1: Pilot density and local bandwidth factors estimation"
		qui akdensity0 `ix' [`weight'`exp'] if `use' , at(`m') generate(`tmpd') /*
		   */ width(`wwidth') lambda(`tmplambda') double  `kernel'
		qui gen double `tmph' = (`wwidth')*(`tmplambda')
		di as text "Step 2: Adaptive kernel density estimation"
		qui akdensity0 `ix' [`weight'`exp'] if `use' , at(`m') generate(`d') /*
		   */ width(`tmph') `nbands' double   `kernel' `cdfopt'
        }
	else {
		di as text "Standard kernel density estimation"
		qui akdensity0 `ix' [`weight'`exp'] if `use' , at(`m') generate(`d') /*
		   */ width(`wwidth') double `nbands'   `kernel' `cdfopt'
        }

	label var `d' `"`yl'"'
	label var `m' `"`ixl'"'

    if (`stdbands')>0 {
      	label var `d'_up `"Variability bands upper limit"'
      	label var `d'_lo `"Variability bands lower limit"'
        loc varbands "`d'_up `d'_lo"
        }

	qui summ `d' in 1/`n', meanonly
	local scale = 1/(`n'*r(mean))

	if `"`density'"' != `""' {
		qui replace `d' = `d'*`scale' in 1/`n'
	}

    tempname tmp1 tmp2 tmp3

   	if _caller() < 8 {
      version 7.0
	  if `"`graph'"'==`""' {
		if `"`symbol'"'  == `""' {local symbol `"o"'}
		if `"`connect'"' == `""' {local connect `"l"' }
		if `"`title'"'   == `""' {
			local title `"Kernel Density Estimate"'
		}
		if `"`normal'"' != `""' {
			tempvar znorm
			scalar `tmp1' = 1/sqrt(2*_pi*`ixsig')
			scalar `tmp2' = -0.5/`ixsig'
			qui gen `znorm' = `tmp1'*exp(`tmp2'*(`m'-`ixmean')^2)
			local symbol `"`symbol'i"'
			local connect `"`connect'l"'
			if `"`density'"' != `""' {
				tempvar fz
				qui gen `fz' = sum(`znorm')
				qui replace `znorm' = `znorm'/`fz'[_N]
			}
		}
		if `student' > 0 {
			tempvar tm
			scalar `tmp1' = exp(lngamma((`student'+1)/2)) /*
                                */ / exp(lngamma(`student'/2)) /*
                                */ * 1/sqrt(`student'*_pi)
			scalar `tmp2' = (`student'+1)/2
			scalar `tmp3' = sqrt(`ixsig')
			qui gen `tm' = `tmp1' * 1/((1+((`m'-`ixmean') /*
				*/ / `tmp3' )^2/`student')^`tmp2')
			local symbol `"`symbol'i"'
			local connect `"`connect'l"'
			tempvar ft
			qui gen `ft' = sum(`tm')
			if `"`density'"' != `""' {
				qui replace `tm' = `tm'/`ft'[_N]
			}
			else {
				qui replace `tm' = `tm'/`ft'[_N]/`scale'
			}
		}
      graph `d' `znorm' `tm' `varbands' `m', s(`symbol'ii) c(`connect'l[-]l[-]) /*
		*/ title(`"`title'"') `options'
	   }
    }
    else {
      version 8.0
      if `"`graph'"'==`""' {
		if `"`normal'"' != "" | `student' > 0 {
			sum `m', mean
			if `"`normal'"' != `""' {
				local Ngraph				///
				(function normden(x,`ixmean',`ixsd'),	///
					range(`r(min)' `r(max)')	///
					yvarlabel("Normal density")	///
					`normopts'			///
				)
			}
		    if `student' > 0 {
				local Tgraph				///
				(function				///
					tden = 				///
					tden(`student',			///
						(x-`ixmean')/`ixsd'	///
					)/`ixsd'			///
				,					///
					range(`r(min)' `r(max)')	///
					yvarlabel(			///
				`"t density, df = `student'"'		///
					)				///
					`stopts'			///
				)
			}
		}

		if (`stdbands')>0 {
		  local BNDSgraph				///
		    (line `d'_up `m',					///
			 ytitle(`"Density (with `stdbands'*S.E. bands)"')			///
		     )						///
		    (line `d'_lo `m')
          }

		graph twoway					///
		(line `d' `m',					///
			ytitle(`"Density"')			///
			xtitle(`"`ixl'"')			///
			legend(cols(1))				///
			`options'				///
		)						///
		`BNDSgraph'					///
		`Ngraph'					///
		`Tgraph'					///
		|| `plot'  || `addplot'				///
		// blank
	  }
    }

   version 7.0

	/* double save in S_# and r() */
	ret clear
	ret local kernel `"`kernel'"'
	ret scalar width = `wwidth'
	ret scalar n = `n'           /* (sic) */
	ret scalar scale = `scale'
	ret scalar stdband = `stdbands'

	global S_1   `"`kernel'"'
	global S_3 = `wwidth'
	global S_2 = `n'
	global S_4 = `scale'
	global S_5 = `stdbands'

   if `nsave' == 1 {
		label var `d' `"density: `ixl'"'
		rename `d' `yl'
	}
	else if `nsave' == 2 {
		label var `m' `"`ixl'"'
		label var `d' `"density: `ixl'"'
		rename `d' `yl'
		rename `m' `xl'
	}

	if ("`cdf'" != "") {
		lab var `cdf' `"smooth cdf: `ixl'"'
	}

   if (inlist(`nsave',1,2)) & (`stdbands'>0) {
       	label var `d'_up `"density: `ixl' (variability bands upper limit)"'
       	label var `d'_lo `"density: `ixl' (variability bands lower limit)"'
		rename `d'_lo `yl'_lo
		rename `d'_up `yl'_up
		if ("`cdf'" != "") {
	       	label var `cdf'_up `"smooth cdf: `ixl' (variability bands upper limit)"'
   	    	label var `cdf'_lo `"smooth cdf: `ixl' (variability bands lower limit)"'
			}
       	}

	if "`at'" != "" {
		sort `srtlist' `obssrt'
	}
end
** added 2011-11-18 (to update to Stata 11 -kernel()- option)
** copied from kdensity.ado with adjustment to allow only epan epan2 gauss
// parsing facility to retrieve kernel name
program _get_kernel_name, sclass
	syntax , KERNEL(string)
	local kernlist epan2 epanechnikov gaussian
	local maxabbrev 5 2 3
	tokenize `maxabbrev'
	local i = 1
	foreach kern of local kernlist {
		if substr("`kern'",1,length(`"`kernel'"')) == `"`kernel'"' ///
					     & length(`"`kernel'"') >= ``i'' {
			sreturn local kernel `kern'
			continue, break
		}
		else {
			sreturn local kernel
		}
		local ++i
	}
end