*! version 1.2.0 MLB 12Dec2013
program define adjustrcspline, sortpreserve rclass
	version 10

	syntax [if] [in],                 ///
	[                                 ///
	at(str asis)                      ///
	link(str)                         /// manually specify link if e(cmd) isn't recognized
	CUSTominvlink(string)             /// manually specify a custom link
	EQuation(passthru)                ///
	CIopts(str asis)                  ///
	noci                              ///
	LINEopts(str asis)                ///
	level(integer  $S_level)          ///
	YTItle(passthru)                  ///
	legend(passthru)                  ///
	addplot(str asis)                 /// extra overlaid graph
	GENerate(namelist max=3)          ///
	*                                 /// other -twoway options
	]

	marksample touse
// Checks
	local splines : char _dta[rcsplines]
	local oldvar : char _dta[oldvar]
	local knots : char _dta[knots]

	if "`splines'" == "" | "`oldvar'" == "" | "`knots'" == "" {
		di as err "adjustrcspline can only be used when the spline was created with mkspine2 with the cubic option"
		exit 198
	}

	capture confirm variable `oldvar'
	if _rc {
		di as err "the original variable on which the spline is based needs to be present in the dataset"
		exit 198
	}

	tempname b
	matrix `b' = e(b)
	local indepvars : colnames `b'

	local ok : list splines in indepvars
	if !`ok' {
		di as err "all variables created in the last call to mkspline2 must be"
		di as err "independent variables in the last estimation command"
		exit 198
	}

	capture assert `:word count `generate'' != 2
	if _rc {
		di as err "only 1 or 3 new variables can be specified in the generate option"
		exit 198
	}
	if "`generate'" != "" {
		confirm new variable `generate'
	}

	if `"`equation'"' != "" {
		local eq `"[`equation']"'
	}
	if "`link'" != "" & "`custominvlink'" != "" {
		di as err "the link() and custominvlink() cannot be specified together"
		exit 198
	}

	// weights
	if "`e(wtype)'" != "" {
		if "`e(wtype)'" == "pweight" {
			local weight "[aweight`e(wexp)']"
		}
		else {
			local weight "[`e(wtype)'`e(wexp)']"
		}
	}

// Collect and check the link function
	if "`link'" != "" {
		// allow case insensitive link names
		local link = lower("`link'")

		// allow abreviations
		local l = max(4,length("`link'"))

		if "`link'" == substr("log", 1, `l') {
					local link = "log"
		}
		else if "`link'" == substr("identity", 1, `l') {
			local link = "identity"
		}
		else if "`link'" == substr("logit", 1, `l') {
			local link = "logit"
		}
		else if "`link'" == substr("probit", 1, `l') {
			local link = "probit"
		}
		else if "`link'" == substr("logcomplement",1,`l') {
			local link = "logcomplement"
		}
		else if "`link'" == substr("loglog", 1, `l') {
			local link = "loglog"
		}
		else if "`link'" == substr("cloglog", 1, `l') {
			local link = "cloglog"
		}
		else if "`link'" == substr("reciprocal", 1, `l') {
			local link = "reciprocal"
		}
		else if "`: word 1 of `link''" == substr("power", 1, `l') {
			capture confirm numeric `: word 2 of `link''
			if _rc {
				di as error "the second element specified in the link() option should be a number when specifying the power link "
				exit 198
			}
			if `: word 2 of `link'' == 0 {
				local link "log"
			}
			else {
				local link = "power `: word 2 of `link''"
			}
		}
		else if "`: word 1 of `link''" == substr("opower", 1, `l') {
			capture confirm numeric `: word 2 of `link''
			if _rc {
				di as error "the second element specified in the link() option should be a number when specifying the odds power link "
				exit 198
			}
			if `: word 2 of `link'' == 0 {
				local link "logit"
			}
			else {
				local link = "opower `: word 2 of `link''"
			}
		}
		else {
			di as err "link `link' not recognized"
			exit 198
		}
	}
	else if "`custominvlink'" == "" {
		if inlist("`e(cmd)'", "regress") | ///
		   ("`e(cmd)'" == "glm" & "`e(linkt)'" == "Identity") {
			local link "identity"
		}
		if inlist("`e(cmd)'", "logit",      ///
	                              "logistic",   ///
	                              "betafit",    ///
	                              "seqlogit") | ///
	           ("`e(cmd)'" == "glm" & "`e(linkt)'" == "Logit") {
	        	local link "logit"
	        }
	        if inlist("`e(cmd)'", "probit") | ///
	           ("`e(cmd)'" == "glm" & "`e(linkt)'" == "Probit") {
	        	local link = "probit"
	        }
	        if inlist("`e(cmd)'", "poisson", "nbreg") |  ///
	           ("`e(cmd)'" == "glm" & "`e(linkt)'" == "Log") {
	        	local link = "log"
	        }
	        if "`e(cmd)'" == "glm" & "`e(linkt)'" == "Log complement" {
	        	local link = "logcomplement"
	        }
	        if "`e(cmd)'" == "glm" & "`e(linkt)'" == "Log-log" {
	        	local link = "loglog"
	        }
	        if ("`e(cmd)'" == "glm" & "`e(linkt)'" == "Complementary log-log") | ///
	           "`e(cmd)'" == "cloglog" {
	        	local link = "cloglog"
	        }
	        if "`e(cmd)'" == "glm" & "`e(linkt)'" == "Reciprocal" {
	        	local link = "reciprocal"
	        }
	        if "`e(cmd)'" == "glm" & strpos("`e(linkt)'", "Power") == 1 {
	        	local link = "power `e(power)'"
	        }
	        if "`e(cmd)'" == "glm" & "`e(linkt)'" == "Odds power" {
	        	local link = "opower `e(power)'"
	        }
	}

	if "`link'`custominvlink'" == "" & "`e(cmd)'" != "glm" {
		di as err "last estimation command (`e(cmd)') was not recognized"
		di as err "specify the link() or custominvlink() option to manually specify the link function"
		exit 198
	}
	if "`link'`custominvlink'" == "" & "`e(cmd)'" == "glm" {
		di as err "the `e(linkt)' link function used in the last glm command was not recognized"
		di as err "specify the link() or custominvlink() option to manually specify the link function"
		exit 198
	}

// Use -predictnl- to create linear predictor and it's CI


	if c(stata_version) >= 11 {
		foreach coef of local indepvars {
			_ms_parse_parts `coef'
			if inlist("`r(type)'", "variable", "factor") & !r(omit) local other "`other' `r(name)'"
			if "`r(type)'" == "factor" & !r(omit) local factor "`factor' `r(name)'"
			if "`r(type)'" == "interaction" {
				local intvar ""
				forvalues i = 1/`r(k_names)' {
					local intvar "`r(name`i')'"
					if `: list intvar in splines' {
						di as err "the spline variables may not be part of an interaction"
						exit 198
					}
				}
			}
		}
		local cons "_cons"
		local other : list other - cons
		local factor : list uniq factor
	}
	else {
		local cons "_cons"
		local other : list indepvars - cons
	}
	local other : list uniq other
	local other : list other - splines


	// local notis created because "=" sign might be attached to variable name without space
	local notis : subinstr local at "=" " ", all
	local other : list other - notis

	// gen id var to merge the new vars into the original dataset
	tempvar id
	qui gen long `id' = _n

	preserve
	tempname atmat

	// take care of offset and exposure
	if `"`e(offset)'"' != "" {
		local xb `"(xb() + `e(offset)')"'
		tempvar offvar
		qui gen double `offvar' = `e(offset)'
		local expvar `e(offset)'
		// remove "ln(" and ")" in case -exposure()-  was specified instead of offset
		local expvar1 : subinstr local expvar "ln(" "", all
		if "`expvar1'" != "`expvar'" {
			local exposure "exposure"
		}
		local expvar : subinstr local expvar1 ")" "", all
		if !`: list offvar in notis' {
			sum `offvar' if `touse', meanonly
			if "`exposure'" != "" {
				qui replace `expvar' = exp(r(mean))
				matrix `atmat' = exp(r(mean))
			}
			else {
				qui replace `expvar' = r(mean)
				matrix `atmat' = r(mean)
			}
			local atmatrow "`expvar'"
		}
	}
	else {
		local xb "xb()"
	}

	// fix control variables not specified in at() at the default values:
	// "left-most mode" if a factor variable
	// mean if other variable
	if "`other'" != "" {
		foreach var of local other {
			if `: list var in factor' {
				Leftmode `var' if `touse' `weight'
				tempvar t
				qui gen double `t' = r(lmode)
				qui drop `var'
				rename `t' `var'
				matrix `atmat' = nullmat(`atmat') \ `=r(lmode)'
				local atmatrow "`atmatrow' `var'"
			}
			else {
				sum `var' if `touse' `weight', meanonly
				tempvar t
				qui gen double `t' = r(mean)
				qui drop `var'
				rename `t' `var'
				matrix `atmat' = nullmat(`atmat') \ `=r(mean)'
				local atmatrow "`atmatrow' `var'"
			}
		}
	}

	// fix the controll variables as specified in the at() option
	local at : subinstr local at " =" "=", all
	local at : subinstr local at "= " "=", all
	local k_at : word count `at'
	tokenize `at'
	forvalues i = 1/`k_at' {
		gettoken var val: `i', parse("=")
		qui drop `var'
		qui gen double ``i''
		matrix `atmat' = nullmat(`atmat') \ ``val''
		local atmatrow "`atmatrow' `var'"
	}

	// do the predictions
	tempvar mu lb ub

	if "`link'" == "identity" {
		qui predictnl double `mu' = `xb' if `touse', ci(`lb' `ub')
	}
	if "`link'" == "logit" {
		qui predictnl double `mu' = invlogit(`xb') if `touse', ci(`lb' `ub')
	}
	if "`link'" == "probit" {
		qui predictnl double `mu' = normal(`xb') if `touse', ci(`lb' `ub')
	}
	if "`link'" == "log" {
		qui predictnl double `mu' = exp(`xb') if `touse', ci(`lb' `ub')
	}
	if "`link'" == "logcomplement" {
		qui predictnl double `mu' = 1 - exp(`xb') if `touse', ci(`lb' `ub')
	}
	if "`link'" == "loglog" {
		qui predictnl double `mu' = exp(-exp(-`xb')) if `touse', ci(`lb' `ub')
	}
	if "`link'" == "cloglog" {
		qui predictnl double `mu' = invcloglog(`xb') if `touse', ci(`lb' `ub')
	}
	if "`link'" == "reciprocal" {
		qui predictnl `mu' = 1/`xb' if `touse', ci(`lb' `ub')
	}
	if "`: word 1 of `link''" == "power" {
		local power : word 2 of `link'
		qui predictnl double `mu' = `xb'^(1/`power') if `touse', ci(`lb' `ub')
	}
	if "`: word 1 of `link''" == "opower" {
		local power : word 2 of `link'
		qui predictnl double `mu' = (1 + `power'*`xb')^(1/`power') / (1 + (1 + `power'*`xb')^(1/`power')) if `touse', ci(`lb' `ub')
	}

	if "`custominvlink'" != "" {
		qui predictnl double `mu' = `custominvlink' if `touse', ci(`lb' `ub')
	}

// keep the graph small by avoiding plotting the same point more than once
	if `"`plot'"' == `""' {
		tempvar mark
		qui bys `oldvar' `touse' : gen byte `mark' = _n == 1
		local if "if `mark'"
	}

// display graph
	if `"`ytitle'"' == "" {
		local ytitle `"ytitle("E(`e(depvar)')")"'
	}
	if `"`legend'"' == "" {
		local legend "legend(nodraw)"
	}

	if "`ci'" == "" {
		twoway rarea `lb' `ub' `oldvar' `if',        ///
		       sort                                  ///
		       `ciopts' ||                           ///
		       line `mu' `oldvar' `if' ,             ///
		       sort                                  ///
		       lpattern(solid)                       ///
		       `ytitle'                              ///
		       `shknots'                             ///
		       `lineopts'                            ///
               `legend'                              ///
		       `options'                             ///
		       || `addplot'
	}
	else {
		twoway line `mu' `oldvar' `if',              ///
		       sort                                  ///
		       lpattern(solid)                       ///
		       `ytitle'                              ///
		       `shknots'                             ///
		       `lineopts'                            ///
		       `legend'                              ///
		       `options'                             ///
		       || `addplot'
	}
// Generate new variables if requested
	if "`generate'" != "" {
		tempfile newvars
		qui keep `id' `mu' `lb' `ub'
		qui sort `id'
		qui save `newvars'
		restore
		sort `id'
		tempvar _merge
		qui merge `id' using `newvars', _merge(`_merge')
	}
	else {
		qui restore
	}

	if `: word count `generate'' == 1 {
		gen double `generate' = `mu'
	}
	if `: word count `generate'' == 3 {
		tokenize `generate'
		gen double `1' = `mu'
		gen double `2' = `lb'
		gen double `3' = `ub'
	}
	capture confirm matrix `atmat'
	if !_rc {
		matrix rownames `atmat' = `atmatrow'
		matrix colnames `atmat' = "value"
		return matrix atmat = `atmat'
	}

end

// based on modes by Nicholas J. Cox:
// NJC 1.4.0 17 November 2009       (SJ9-4: sg113_2)
program define Leftmode, sort rclass
	syntax varname [if]
	marksample touse

	tempvar freq minus
	quietly {
		bys `touse' `varlist' : gen `freq' = sum(`touse')*`touse'
		by  `touse' `varlist' : replace `freq' = (_n == _N)*`freq'[_N]
		gen `minus' = -`varlist'
		sort `touse' `freq' `minus'
	}
	return scalar lmode = `varlist'[_N]
end