*! v 3.0.0 PR 24Jul2000.
program define centcalc, sclass
version 6
local hidopts "TRunc TAu LNs"
syntax varlist(min=2 max=2) [if] [in] [, Centiles(numlist >0 <100) /*
 */ DIst(string) Prefix(string) Gamma(string) DElta(string) CV `hidopts' ]
if "`dist'"=="" { local dist n }
else local dist = lower(substr("`dist'", 1, 2))
if "`dist'"=="n"|"`dist'"=="no" {
	local dnum 0
}
else local dnum=1*("`dist'"=="sl")+2*("`dist'"=="pn") /*
 */	       +3*("`dist'"=="en")+4*("`dist'"=="eg") /*
 */ 	       +5*("`dist'"=="ep")+6*("`dist'"=="ee") /*
 */ 	       +7*("`dist'"=="mp")+8*("`dist'"=="me")
local pars4=(`dnum'>=5)
if `dnum'>0 {
	if "`gamma'"=="" {
		di in red "gamma() required for `dist' distribution"
		exit 198
	}
	cap confirm var `gamma'
	if _rc { confirm num `gamma' }
	if `pars4' {
		if "`delta'"=="" {
			di in red "delta() required for `dist' distribution"
			exit 198
		}
		cap confirm var `delta'
		if _rc { confirm num `delta' }
	}
}
/*
	mu is M curve.
	sfit is fitted S curve, could be cv, log sigma, etc.
*/
gettoken mu sfit: varlist
quietly {

* deal with user prefix for centile variable(s)
	if "`prefix'"=="" { local prefix "C" }

* parse the centiles
	if "`centile'"=="" {
		local nc 1
		local c1 50
	}
	else {
		local nc 0 /* counter to index strings containing centiles */
		tokenize "`centile'"
		while "`1'"!="" {
			local nc=`nc'+1
			local c`nc' `1'
			mac shift
		}
	}
/*
	Estimate the centiles.
	sigma is the scale (S) parameter.
*/
	if "`cv'"=="" & "`lns'"=="" {
		local sigma `sfit'	/* sigma parametrization */
	}
	else {
		tempvar sigma
		if "`lns'"!="" {	/* log S parametrization */
			if "`cv'"!="" {
				gen double `sigma' = `mu'*exp(`sfit')
			}
			else gen double `sigma' = exp(`sfit')
		}
		else gen double `sigma' = `mu'*`sfit'
	}
	if "`tau'"!="" & (`dnum'==2|`dnum'==5) { /* PN, tau parametrization */
		tempvar g			 /* lambda */
		gen `g' = 1-`gamma'*`mu'/`sigma'
	}
	else local g `gamma'
	local i 0
	while `i'<`nc' {
		local i=`i'+1
		centcal `dnum' `mu' `sigma' "`g'" "`delta'" /*
		 */ `c`i'' `prefix' "`trunc'"
		sret local cvar`i' `s(cname)'
	}
}
end

program define centcal, sclass /* based on freestanding _centcal v 1.0.3 28-Feb-95. */
* args: 1=delta, 2=gamma, 3=mu, 4=sigma, 5=centile value,
* 6=variable to hold estimated centile,
* 7=distribution (0=normal, 1=SL, 2=PN, 3=EN, 4=EG, 5=EP, 6=EE, 7=MP, 8=ME),
* 8=trunc flag.
	local dnum `1'
	local mu `2'
	local sigma `3'
	local gamma "`4'"
	local delta "`5'"
	local centile `6'
	local C `7'
	local trunc = "`8'"!=""

	tempvar A B

	local q = `centile'/100

	if `centile'>0 {
* Compute z-value from user's centile
		local z=invnorm(`q')
		local c `C'`centile'    /* name of centile var incs centile */
* fix up name if contains a dec point
		local pt=index("`c'",".")
		if `pt' {
			local c=substr(substr("`c'",1,`pt'-1) /*
			 */ +"_"+substr("`c'",`pt'+1,.),1,8)
		}
	}
	else {
		local z=invnorm(-`q')
		local c `C'             /* user-supplied name if centile<0 */
	}

* Estimate centile
	if `trunc' { tempvar uq }
	else local uq `z'
	cap drop `c'
	local small 1e-6
	if `dnum'==0 {				/* normal */
		if `trunc' {
			gen `uq' = invnorm( `q'+(1-`q')* /*
			 */ normprob(-`mu'/`sigma') )
		}
		gen `c' = `mu'+`sigma'*`uq'
	}
	else if `dnum'==1 {			/* SL */
		if `trunc' {
			gen `uq' = cond(abs(1-`mu'*`gamma'/`sigma')<`small', /*
			 */ `z', /*
			 */ cond(abs(`gamma')<`small', /*
			 */ invnorm( `q'+(1-`q')* /*
			 */ normprob(-`mu'/`sigma') ), /*
			 */ invnorm( `q'+(1-`q')* /*
			 */ normprob(log(1-`mu'*`gamma'/`sigma')/`gamma')) ))
		}
		gen `c'=cond(abs(`gamma')<`small', /*
		 */ `mu'+`sigma'*`uq', /*
		 */ `mu'+(exp(`gamma'*`uq')-1)*`sigma'/`gamma' )
	}
	else if `dnum'==2 {			/* PN */
		if `trunc' {
			gen `A' = cond(`gamma'<`small', /*
			 */ 0, normprob(-`mu'/(`sigma'*`gamma')) )
			gen `B' = cond(`gamma'<-`small', /*
			 */ normprob(-`mu'/(`sigma'*`gamma')), 1)
			gen `uq' = invnorm(`q'*`B'+(1-`q')*`A')
		}
		gen `c'=cond(abs(`gamma')<`small', /*
		 */ `mu'*exp(`sigma'*`uq'/`mu'), /*
		 */ `mu'*(1+`gamma'*`sigma'*`uq'/`mu')^(1/`gamma') )
	}
	else if `dnum'==3 {			/* EN */
		if `trunc' {
			gen `A' = cond(`gamma'<`small', /*
			 */ 0, normprob(-1/`gamma') )
			gen `B' = cond(`gamma'<-`small', /*
			 */ normprob(-1/`gamma'), 1 )
			gen `uq' = invnorm(`q'*`B'+(1-`q')*`A')
		}
		gen `c'=cond(abs(`gamma')<`small', /*
		 */ `mu'+`sigma'*`uq', /*
		 */ `mu'+`sigma'*log(1+`gamma'*`uq')/`gamma' )
	}
	else if `dnum'==4 {			/* EG */
		gen double `A'=(`gamma')^(-2) 	/* theta = kappa^-2 */
		gen `B'=cond(`gamma'<0, invgammap(`A',1-`q'), invgammap(`A',`q'))
		gen `c'=cond(abs(`gamma')<0.01, /*
		 */ `mu'+`sigma'*`uq', /*
		 */ `mu'+`sigma'*(ln(`B'/`A')/`gamma'))
	}
	else if `dnum'==5 {			/* EP */
		tempvar G
		gen `G' = `gamma'*`sigma'/`mu' /* back to gamma param. */
		if `trunc' {
			_eetrunc `G' `delta' `A' `B'
			_epccalc `mu' `sigma' `gamma' `delta' `q' `A' `B' `c'
		}
		else {
			_epccalc `mu' `sigma' `gamma' `delta' `q' 0 1 `c'
		}
	}
	else if `dnum'==6 {			/* EE */
		tempvar D
		gen `D' = `delta'-`gamma'	/* delta, from eta */
		if `trunc' {
			_eetrunc `gamma' `D' `A' `B'
			_eeccalc `mu' `sigma' `gamma' `D' `q' `A' `B' `c'
		}
		else {
			_eeccalc `mu' `sigma' `gamma' `D' `q' 0 1 `c'
		}
	}
	else if `dnum'==7 {			/* MPN */
		if `trunc' {
			tempvar eps
			gen `eps' = -`mu'/(`sigma'*`gamma')
			nmod `eps' `delta'
			gen `A' = cond(`gamma'<`small', 0, `eps')
			gen `B' = cond(`gamma'<-`small', `eps', 1)
			_mpccalc `mu' `sigma' `gamma' `delta' `q' `A' `B' `c'
		}
		else {
			_mpccalc `mu' `sigma' `gamma' `delta' `q' 0 1 `c'
		}
	}
	else if `dnum'==8 {			/* MEN */
		if `trunc' {
			gen `A' = cond(`gamma'<`small', -99, -1/`gamma')
			gen `B' = cond(`gamma'<-`small', -1/`gamma', 99)
			nmod `A' `delta'
			nmod `B' `delta'
			_meccalc `mu' `sigma' `gamma' `delta' `q' `A' `B' `c'
		}
		else {
			_meccalc `mu' `sigma' `gamma' `delta' `q' 0 1 `c'
		}
	}
	sret local cname `c'
end

program define nmod /* normprob of modulus function */
	replace `1'= normprob(sign(`1')*((abs(`1')+1)^`2'-1)/`2')
end
*! v 1.0.0 PR 12Sep95.
program define _meccalc /* calc centile for truncated EEN dist. */
	local mu `1'
	local sigma `2'
	local G `3'	/* gamma parameter---not lambda */
	local D `4'	/* delta power parameter */
	local q `5'	/* desired quantile, e.g. .95 */
	local A `6' 	/* existing, lower limit in prob scale */
	local B `7' 	/* existing, upper limit in prob scale */
	local c `8'	/* existing var to hold centile */
	tempname small
	scalar `small'=1e-6
	tempvar u zq
	gen `zq' = invnorm(`q'*`B'+(1-`q')*`A')
	gen `u' = sign(`zq')*cond(abs(`D')<`small', exp(abs(`zq'))-1, /*
	 */ (1+`D'*abs(`zq'))^(1/`D')-1 )
	gen `c'=`mu'+`sigma'*cond(abs(`G')<`small', `u', log(1+`G'*`u')/`G' )
end
*! v 1.0.0 PR 12Sep95.
program define _mpccalc /* calc centile for truncated MPN dist. */
	local mu `1'
	local sigma `2'	 /* scale, not CV */
	local lambda `3' /* called gamma elsewhere */
	local D `4'	 /* delta power parameter */
	local q `5'	 /* desired quantile(s), e.g. .95 */
	local A `6' 	 /* existing, lower limit in prob scale */
	local B `7' 	 /* existing, upper limit in prob scale */
	local c `8'	 /* existing var to hold centile */
	tempname small
	scalar `small'=1e-6
	tempvar u zq
	gen `zq' = invnorm(`q'*`B'+(1-`q')*`A')
	gen `u' = sign(`zq')*cond(abs(`D')<`small', exp(abs(`zq'))-1, /*
	 */ (1+`D'*abs(`zq'))^(1/`D')-1 )
	gen `c'=`mu'*cond(abs(`lambda')<`small', /*
	 */ exp(`sigma'*`u'/`mu'), /*
	 */ (1+`lambda'*`sigma'*`u'/`mu')^(1/`lambda') )
end