capture program drop stpm2illd
program define stpm2illd
        version 11.1


syntax newvarlist (min=3 max=4), TRANS1(string) TRANS2(string) TRANS3(string) ///
                 [OBS(int 1000) CI MINT(real 0) MAXT(real 0) TIMEname(string) HAZard HAZNAME(string) ITERATE(int 100) COMBINE]


local newvarlist `varlist'

tokenize `newvarlist'
local i=0
while "`1'"!="" {
  local i=`i'+1
  local newvar`i' `1'
  mac shift 1
}
local newvarlistcount `i'

if "`hazname'"!="" {
	tokenize `hazname'
	local i=0
	while "`1'"!="" {
		local i=`i'+1
		local haz`i' `1'
		mac shift 1
	}
	if `i'<3 {
		di as err "Must specify three variable names for the transition hazards in the hazname option."
	}
}

if "`hazname'"=="" {
	local haz1 trans1
	local haz2 trans2
	local haz3 trans3
}

*** Check that haznames don't already exist in the data. ***
if "`hazard'"!="" {
	capture confirm variable h_`haz1'
	if _rc==0 {
		di as err "h_`haz1' already exists in the dataset. Use the hazname option to change the name of the transition hazards."
        exit
	}
	capture confirm variable h_`haz2'
	if _rc==0 {
		di as err "h_`haz2' already exists in the dataset. Use the hazname option to change the name of the transition hazards."
        exit
	}
	capture confirm variable h_`haz3'
	if _rc==0 {
		di as err "h_`haz3' already exists in the dataset. Use the hazname option to change the name of the transition hazards."
        exit
	}
}

*** Check that timename doesn't already exist if option is used. ***
if "`timename'"!=""{
	capture confirm variable `timename'
	if _rc==0 {
		di as err "`timename' already exists in the dataset"
        exit
  }
}


*** Check that the correct number has been specified in newvarlist. ***
if "`combine'"=="" & `newvarlistcount'==3 {
	di as err "You need to specify four names in newvarlist unless you are using the combine option"
}

if "`combine'"!="" & `newvarlistcount'==4 {
	di as err "You only need to specify three names in newvarlist when using the combine option"
}

*** Set default minumum and maximum times according to _t from stset. ***
if "`mint'"=="0" {
qui sum _t
local mint=r(min)
}
if "`maxt'"=="0" {
qui sum _t
local maxt=r(max)
}


*** Save tvc variables in local macro. ***
local etvc `e(tvc)'

*** Count how many variables have been specified for each transition and store in local macros. ***
forvalues i=1/3 {
  local varcount`i' : word count `trans`i''
  local count`i'=`varcount`i''/2
  local j=0
  tokenize `trans`i''
  while "`1'"!="" {
        local j=`j'+1
    unab 1: `1'
    cap confirm var `2'
    if _rc {
          cap confirm num `2'
      if _rc {
      di in red "invalid at(... `1' `2' ...)"
      exit 198
      }
    }
        local cov`i'`j' `1'
        local covval`i'`j' `2'
        mac shift 2
  }
}



*** Create a tempfile. ***
tempfile ind
preserve
drop _all
qui set obs `obs'
tempvar t
range `t' `mint' `maxt'
tempvar lnt
qui gen `lnt' = ln(`t')


*** Calculate baseline splines. ***
if "`e(rcsbaseoff)'" == "" {
  capture drop _rcs`i'* _d_rcs`i'*
  if "`e(orthog)'" != "" {
        matrix R = e(R_bh)
        local rmatrix rmatrix(R)
  }
  qui rcsgen `lnt', knots(`e(ln_bhknots)') gen(_rcs) dgen(_d_rcs) `e(reverse)' `rmatrix'

  forvalues i=1/`e(dfbase)' {
    local rcs `rcs' [xb][_rcs`i']*_rcs`i'
    local d_rcs `d_rcs' [dxb][_d_rcs`i']*_d_rcs`i'

        if `i'!=`e(dfbase)' {
          local rcs `rcs' +
          local d_rcs `d_rcs' +
        }
  }
}

*** Find out how many tvc term there are for each transition. ***
forvalues i=1/3 {
  local p=0
  forvalues j=1/`count`i'' {
    if `"`: list posof `"`cov`i'`j''"' in etvc'"' != "0" {

          local p=`p'+1
        }
  }
  local tvcno`i'=`p'
}


*** Store splines in local macros for each transition. ***
forvalues i=1/3 {

  if "`e(rcsbaseoff)'" == "" {
        local rcs`i' `rcs`i'' `rcs'
        local d_rcs`i' `d_rcs`i'' `d_rcs'
  }

  local m=0
  forvalues j=1/`count`i'' {
    local p=`m'
    if `"`: list posof `"`cov`i'`j''"' in etvc'"' != "0" {

          local m=`m'+1

          if `m'==1 & "`e(rcsbaseoff)'" == "" {
            local rcs`i' `rcs`i'' +
            local d_rcs`i' `d_rcs`i'' +
          }

      local tvcvar`i'`j' `cov`i'`j''
          capture drop _rcs_`tvcvar`i'`j''* _d_rcs_`tvcvar`i'`j''*
          if "`e(orthog)'" != "" {
            matrix R`i'`j' = e(R_`tvcvar`i'`j'')
            local rmatrix rmatrix(R`i'`j')
          }
      qui rcsgen `lnt', knots(`e(ln_tvcknots_`tvcvar`i'`j'')') gen(_rcs_`tvcvar`i'`j'') dgen(_d_rcs_`tvcvar`i'`j'') `e(reverse)' `rmatrix'

          forvalues l = 1/`e(df_`tvcvar`i'`j'')' {

            local rcs`i'`j' `rcs`i'`j'' ([xb][_rcs_`tvcvar`i'`j''`l']*_rcs_`tvcvar`i'`j''`l'*`covval`i'`j'')
            local d_rcs`i'`j' `d_rcs`i'`j'' ([dxb][_d_rcs_`tvcvar`i'`j''`l']*_d_rcs_`tvcvar`i'`j''`l'*`covval`i'`j'')

                if `l'!=`e(df_`tvcvar`i'`j'')' {
                  local rcs`i'`j' `rcs`i'`j'' +
                  local d_rcs`i'`j' `d_rcs`i'`j'' +
                }
          }
        }

        if `p'!=`m' {
      local rcs`i' `rcs`i'' `rcs`i'`j''
      local d_rcs`i' `d_rcs`i'' `d_rcs`i'`j''
          if `m'!=`tvcno`i'' {
            local rcs`i' `rcs`i'' +
            local d_rcs`i' `d_rcs`i'' +
          }
    }
  }

  *** Add constant to splines macro if nocons option is not specified with stpm2. ***
  if "`e(noconstant)'" == "" {
    local rcs`i' `rcs`i'' + [xb][_cons]
  }

}

*** Store covariates in local macros for each transition. ***
forvalues i=1/3 {
  forvalues j=1/`count`i'' {
    local covs`i' `covs`i'' ([xb][`cov`i'`j'']*`covval`i'`j'')
        if `j'!=`count`i'' {
          local covs`i' `covs`i'' +
    }
  }
}

*** Calculate length of interval. ***
local step = `maxt'/(`obs' - 1)

forvalues i=1/3 {
  local xb`i' `covs`i''+`rcs`i''
}


*** Calculate hazard functions for each transition. ***
forvalues i=1/3 {
  local h_`haz`i'' (1/`t')*(`d_rcs`i'')*exp(`xb`i'')
  qui gen h_`haz`i''=(1/`t')*(`d_rcs`i'')*exp(`xb`i'')
}



*** Calculate probability of being alive - state 1. ***
if "`ci'"!="" {
  local ci_`newvar1' ci(prob_`newvar1'_lci prob_`newvar1'_uci)
}
qui predictnl prob_`newvar1' = exp(-exp(`xb1'))*exp(-exp(`xb2')), `ci_`newvar1'' force iterate(`iterate')



*** Calculate probability of being ill - state 2. ***

if "`ci'"!="" {
  local ci_`newvar2' ci(prob_`newvar2'_lci prob_`newvar2'_uci)
}

qui predictnl prob_`newvar2' = sum(`step'*((`h_`haz2''*exp(-exp(`xb1'))*exp(-exp(`xb2')))/(exp(-exp(`xb3')))))*(exp(-exp(`xb3'))), ///
							`ci_`newvar2'' force iterate(`iterate')



*** Calculate probability of being dead - state 3. ***
if "`combine'"=="" {
	if "`ci'"!="" {
		local ci_`newvar3' ci(prob_`newvar3'_lci prob_`newvar3'_uci)
	}

	qui predictnl prob_`newvar3' = sum(`step'*(`h_`haz1''*exp(-exp(`xb1'))*exp(-exp(`xb2')))), `ci_`newvar3'' force iterate(`iterate')
}

*** Calculate probability of being ill then dead - state 4. ***
if "`combine'"=="" {
	if "`ci'"!="" {
		local ci_`newvar4' ci(prob_`newvar4'_lci prob_`newvar4'_uci)
	}

	qui predictnl prob_`newvar4' = 1-(exp(-exp(`xb1'))*exp(-exp(`xb2')))-(sum(`step'*(`h_`haz1''*exp(-exp(`xb1'))*exp(-exp(`xb2'))))) ///
								-sum(`step'*((`h_`haz2''*exp(-exp(`xb1'))*exp(-exp(`xb2')))/(exp(-exp(`xb3')))))*(exp(-exp(`xb3'))), ///
								`ci_`newvar4'' force iterate(`iterate')
}


*** If combine option is specified then create one probability for death. ***
if "`combine'"!="" {
	if "`ci'"!="" {
		local ci_`newvar3' ci(prob_`newvar3'_lci prob_`newvar3'_uci)
	}
	qui predictnl prob_`newvar3'=1-(exp(-exp(`xb1'))*exp(-exp(`xb2')))-sum(`step'*((`h_`haz2''*exp(-exp(`xb1'))* ///
	                             exp(-exp(`xb2')))/(exp(-exp(`xb3')))))*(exp(-exp(`xb3'))), ///
								`ci_`newvar3'' force iterate(`iterate')
}

*** Create a local macro for variables we want to keep. ***
if "`combine'"=="" {
	if "`hazard'"=="" {
		if "`ci'"=="" {
			local probs prob_`newvar1' prob_`newvar2' prob_`newvar3' prob_`newvar4'
		}
		if "`ci'"!="" {
			local probs prob_`newvar1' prob_`newvar1'_lci prob_`newvar1'_uci prob_`newvar2' prob_`newvar2'_lci prob_`newvar2'_uci
			local probs `probs' prob_`newvar3' prob_`newvar3'_lci prob_`newvar3'_uci prob_`newvar4' prob_`newvar4'_lci prob_`newvar4'_uci
		}
	}

	if "`hazard'"!="" {
		if "`ci'"=="" {
			local probs prob_`newvar1' prob_`newvar2' prob_`newvar3' prob_`newvar4' h_`haz1' h_`haz2' h_`haz3'
		}
		if "`ci'"!="" {
			local probs prob_`newvar1' prob_`newvar1'_lci prob_`newvar1'_uci prob_`newvar2' prob_`newvar2'_lci prob_`newvar2'_uci
			local probs `probs' prob_`newvar3' prob_`newvar3'_lci prob_`newvar3'_uci prob_`newvar4' prob_`newvar4'_lci prob_`newvar4'_uci
			local probs `probs' h_`haz1' h_`haz2' h_`haz3'
		}
	}
}

if "`combine'"!="" {
	if "`hazard'"=="" {
		if "`ci'"=="" {
			local probs prob_`newvar1' prob_`newvar2' prob_`newvar3'
		}
		if "`ci'"!="" {
			local probs prob_`newvar1' prob_`newvar1'_lci prob_`newvar1'_uci prob_`newvar2' prob_`newvar2'_lci prob_`newvar2'_uci
			local probs `probs' prob_`newvar3' prob_`newvar3'_lci prob_`newvar3'_uci
		}
	}

	if "`hazard'"!="" {
		if "`ci'"=="" {
			local probs prob_`newvar1' prob_`newvar2' prob_`newvar3' h_`haz1' h_`haz2' h_`haz3'
		}
		if "`ci'"!="" {
			local probs prob_`newvar1' prob_`newvar1'_lci prob_`newvar1'_uci prob_`newvar2' prob_`newvar2'_lci prob_`newvar2'_uci
			local probs `probs' prob_`newvar3' prob_`newvar3'_lci prob_`newvar3'_uci
			local probs `probs' h_`haz1' h_`haz2' h_`haz3'
		}
	}
}



*** Change label for time if timename option specified. ***
if "`timename'"!="" {
  gen `timename'=`t'
  keep `timename' `probs'
}

if "`timename'"=="" {
  gen _newt=`t'
  keep _newt `probs'
}


*** Restore original dataset and merge with tempfile. ***
qui save `ind'
restore
qui merge 1:1 _n using `ind', nogenerate

end