*version 1.0 January 2021
*accommodates different bandwidths and polynomial orders on two sides of the threshold

*version 1.1 July 2022
*Fixed minor bug that returns an error message when kernel is specified as "triangular"

*version 1.2 March 2023
*Updated Stata version number
*Per Kit Baum's suggestion, changed program from eclass to rclass
*These changes do not affect any calculation

set type double
capture program drop rdmses2s
program define rdmses2s, rclass
	version 15.0
	syntax anything [if] [in] [, c(real 0) deriv(real 0) pl(real 1) pr(real 1) hl(real 0) hr(real 0) bl(real 0) br(real 0) kernel(string) scalepar(real 1)]

	marksample touse

	local ql = `pl'+1
	local qr = `pr'+1
	local kernel = lower("`kernel'")
	local matches = 3
	local bwselect = "CCT"

	preserve
	qui keep if `touse'

	tokenize "`anything'"
	local y `1'
	local x `2'

	qui drop if `y'==. | `x'==.

	tempvar x_l x_r y_l y_r uh_l uh_r ub_l ub_r T T_l T_r

	qui gen `x_l' = `x' if `x'<`c'
	qui gen `x_r' = `x' if `x'>=`c'
	qui gen `y_l' = `y' if `x'<`c'
	qui gen `y_r' = `y' if `x'>=`c'
	qui su `x'
	local x_min = r(min)
	local x_max = r(max)
	qui su `x_l'
	local N_l = r(N)
	local range_l = abs(r(max)-r(min))
	qui su `x_r'
	local N_r = r(N)
	local range_r = abs(r(max)-r(min))
	local N = `N_r' + `N_l'
	local m = `matches' + 1

	local pl1 = `pl' + 1
	local pr1 = `pr' + 1
	local ql1 = `ql' + 1
	local qr1 = `qr' + 1

	**************************** ERRORS
	if (`c'<=`x_min' | `c'>=`x_max'){
	 di "{err}{cmd:c()} should be set within the range of `x'"
	 exit 125
	}

	if (`N_l'<20 | `N_r'<20){
	 di "{err}Not enough observations to perform calculations"
	 exit 2001
	}

	if ("`kernel'"~="uni" & "`kernel'"~="uniform" & "`kernel'"~="tri" & "`kernel'"~="triangular" & "`kernel'"~="" ){
	 di "{err}{cmd:kernel()} incorrectly specified"
	 exit 7
	}

	if ("`pl'"<"0" | "`pr'"<"0" | "`deriv'"<"0"){
	 di "{err}{cmd:pl()}, {cmd:pr()}, and {cmd:deriv()} should be positive"
	 exit 411
	}

	if (("`deriv'">"`pl'" | "`deriv'">"`pr'") & "`deriv'">"0" ){
	 di "{err}{cmd:deriv()} can not be higher than {cmd:pl()} or {cmd:pr()}"
	 exit 125
	}

	if ("`pl'">"0" & "`pr'">"0") {
		local pl_round = round(`pl')/`pl'
		local pr_round = round(`pr')/`pr'
		local ql_round = round(`ql')/`ql'
		local qr_round = round(`qr')/`qr'
		local d_round = round(`deriv'+1)/(`deriv'+1)
		local m_round = round(`matches')/`matches'

		if (`pl_round'!=1 | `pr_round'!=1 | `ql_round'!=1 | `qr_round'!=1 | `d_round'!=1 |`m_round'!=1 ){
			di "{err}{cmd:pl()}, {cmd:pr()}, {cmd:deriv()}, and {cmd:matches()} should be integers"
			exit 126
																		}
					}

	if (`pl'>8 | `pr'>8) {
		di "{err}The upper bound of {cmd:pl()} and {cmd:pr()} is 8"
		exit 125
				}


	if ("`hl'"=="0" | "`hr'"=="0" | "`bl'"=="0" | "`br'"=="0" | `hl'==. | `hr'==. | `bl'==. | `br'==.) {
		disp in ye "Bandwidths hl, hr, bl, and br need to be specified"
		exit 198
	}


	if ("`exit'">"0") {
			exit
		}


	if ("`kernel'"=="uniform" | "`kernel'"=="uni") {
			local kernel_type = "Uniform"
		}
	else  {
			local kernel_type = "Triangular"
		}


	if ("`kernel'"=="uniform" | "`kernel'"=="uni") {
			local kid=2
			local C_pilot=1.84
	}
	else  {
			local kid=1
			local C_pilot=2.58
	}

	kconst `ql1' `ql1' `kid'
	local C1_lq = e(C1)
	local C2_lq = e(C2)

	kconst `qr1' `qr1' `kid'
	local C1_rq = e(C1)
	local C2_rq = e(C2)

	** compute bias and variance for the conventional estimator **
	qui su `x', d
	local h_pilot_CCT = `C_pilot'*min(r(sd),(r(p75)-r(p25))/1.349)*r(N)^(-1/5)

	sort `x', stable

	mata{
		Y   = st_data(.,("`y'"),   0)
		X   = st_data(.,("`x'"),   0)
		Y_l = st_data(.,("`y_l'"), 0)
		Y_r = st_data(.,("`y_r'"), 0)
		X_l = st_data(.,("`x_l'"), 0)
		X_r = st_data(.,("`x_r'"), 0)
		c = `c'
		N_l = length(X_l)
		N_r = length(X_r)
		c_l  = J(N_l, 1, c)
		c_r  = J(N_r, 1, c)
		deriv = `deriv'
		pl = `pl'
		pr = `pr'
		ql = `ql'
		qr = `qr'
		pl1 = `pl' + 1
		pr1 = `pr' + 1
		ql1 = `ql' + 1
		qr1 = `qr' + 1
		pl2 = `pl' + 2
		pr2 = `pr' + 2
		ql2 = `ql' + 2
		qr2 = `qr' + 2
		pl3 = `pl' + 3
		pr3 = `pr' + 3
		ql3 = `ql' + 3
		qr3 = `qr' + 3
		h_pilot_CCT=`h_pilot_CCT'
		C1_lq = `C1_lq'
		C2_lq = `C2_lq'
		C1_rq = `C1_rq'
		C2_rq = `C2_rq'

		X_lq2 = J(N_l, ql+3, .);	X_rq2 = J(N_r, qr+3, .)
		for (j=1; j<=ql3; j++) {
			X_lq2[.,j] = (X_l:-c):^(j-1)
		}

		for (j=1; j<=qr3; j++) {
			X_rq2[.,j] = (X_r:-c):^(j-1)
		}

		X_lq1 = X_lq2[.,1::ql2];X_rq1 = X_rq2[.,1::qr2]

		w_pilot_l = kweight(X_l,c,h_pilot_CCT,"`kernel'")
		w_pilot_r = kweight(X_r,c,h_pilot_CCT,"`kernel'")

		Gamma_pilot_lq1 = quadcross(X_lq1, w_pilot_l, X_lq1);	Gamma_pilot_rq1 = quadcross(X_rq1, w_pilot_r, X_rq1)
		invGamma_pilot_lq1 = cholinv(Gamma_pilot_lq1);	invGamma_pilot_rq1 = cholinv(Gamma_pilot_rq1)
		sigma_l_pilot = altrdvce(X_l, Y_l, Y_l, `pl', `h_pilot_CCT', `matches', "`vce'", "`kernel'")
		sigma_r_pilot = altrdvce(X_r, Y_r, Y_r, `pr', `h_pilot_CCT', `matches', "`vce'", "`kernel'")
		Psi_pilot_lq1 = quadcross(X_lq1, w_pilot_l:*sigma_l_pilot:*w_pilot_l, X_lq1)
		Psi_pilot_rq1 = quadcross(X_rq1, w_pilot_r:*sigma_r_pilot:*w_pilot_r, X_rq1)

		V_l_m3_pilot_CCT = (invGamma_pilot_lq1*Psi_pilot_lq1*invGamma_pilot_lq1)[ql2,ql2]
		V_r_m3_pilot_CCT = (invGamma_pilot_rq1*Psi_pilot_rq1*invGamma_pilot_rq1)[qr2,qr2]

		m4_l_pilot_CCT = (cholinv(quadcross(X_lq2,X_lq2))*quadcross(X_lq2,Y_l))[ql3,1]
		m4_r_pilot_CCT = (cholinv(quadcross(X_rq2,X_rq2))*quadcross(X_rq2,Y_r))[qr3,1]

		q_l_CCT = ((2*ql+3)*`N'*`h_pilot_CCT'^(2*ql+3)*V_l_m3_pilot_CCT)/((`N'*(2*(C1_lq*(m4_l_pilot_CCT))^2))^(1/(2*ql+5)))
		q_r_CCT = ((2*qr+3)*`N'*`h_pilot_CCT'^(2*qr+3)*V_r_m3_pilot_CCT)/((`N'*(2*(C1_rq*(m4_r_pilot_CCT))^2))^(1/(2*qr+5)))

		w_q_l=kweight(X_l,c,q_l_CCT,"`kernel'")
		w_q_r=kweight(X_r,c,q_r_CCT,"`kernel'")

		* m3_l and m3_r are m3_l_CCT and m3_r_CCT from the bw programs
		m3_l = (cholinv(quadcross(X_lq1, w_q_l, X_lq1))*quadcross(X_lq1, w_q_l, Y_l))[ql2,1]
		m3_r = (cholinv(quadcross(X_rq1, w_q_r, X_rq1))*quadcross(X_rq1, w_q_r, Y_r))[qr2,1]

		wh_l = kweight(X_l,`c',`hl',"`kernel'");	wh_r = kweight(X_r,`c',`hr',"`kernel'")
		wb_l = kweight(X_l,`c',`bl',"`kernel'");	wb_r = kweight(X_r,`c',`br',"`kernel'")

		uh_l = (X_l-c_l)/`hl';	uh_r = (X_r-c_r)/`hr';
		uhh_l=select(uh_l,wh_l:> 0);	uhh_r=select(uh_r,wh_r:> 0)

		ub_l = (X_l-c_l)/`bl';   ub_r = (X_r-c_r)/`br';
		ubb_l = select(ub_l,wb_l:>0); ubb_r=select(ub_r,wb_r:>0)

		Yh_l  = select(Y_l,  wh_l:> 0);	Yh_r  = select(Y_r,  wh_r:> 0)
		Yb_l  = select(Y_l,  wb_l:> 0);	Yb_r  = select(Y_r,  wb_r:> 0)
		Xh_l  = select(X_l,  wh_l:> 0);	Xh_r  = select(X_r,  wh_r:> 0)
		Xb_l  = select(X_l,  wb_l:> 0);	Xb_r  = select(X_r,  wb_r:> 0)
		whh_l = select(wh_l, wh_l:> 0);	whh_r = select(wh_r, wh_r:> 0)
		wbb_l = select(wb_l, wb_l:> 0);	wbb_r = select(wb_r, wb_r:> 0)

		Nh_l = length(Xh_l);	Nb_l = length(Xb_l)
		Nh_r = length(Xh_r);	Nb_r = length(Xb_r)
		X_lp  = J(N_l,pl1,.);	X_rp  = J(N_r,pr1,.)
		X_lq =  J(N_l,ql1,.);	X_rq =  J(N_r,qr1,.)
		Xh_lp = J(Nh_l,pl1,.);	Xh_rp = J(Nh_r,pr1,.)
		Xb_lq = J(Nb_l,ql1,.);	Xb_rq = J(Nb_r,qr1,.)

		for (j=1; j<=pl1; j++)  {
			X_lp[.,j]  = (X_l:-c):^(j-1)
			Xh_lp[.,j] = (Xh_l:-c):^(j-1)
								}

		for (j=1; j<=pr1; j++)  {
			X_rp[.,j]  = (X_r:-c):^(j-1)
			Xh_rp[.,j] = (Xh_r:-c):^(j-1)
								}

		for (j=1; j<=ql1; j++)  {
			X_lq[.,j]  = (X_l:-c):^(j-1)
			Xb_lq[.,j] = (Xb_l:-c):^(j-1)
								}

		for (j=1; j<=qr1; j++)  {
			X_rq[.,j]  = (X_r:-c):^(j-1)
			Xb_rq[.,j] = (Xb_r:-c):^(j-1)
								}

		st_numscalar("Nh_l", Nh_l[1,1]);	st_numscalar("Nb_l", Nb_l[1,1])
		st_numscalar("Nh_r", Nh_r[1,1]);	st_numscalar("Nb_r", Nb_r[1,1])

		if (Nh_l<5 | Nh_r<5 | Nb_l<5 | Nb_r<5){
			display("{err}Not enough observations to perform calculations")
			exit()
												}

		sigmah_l = altrdvce(Xh_l, Yh_l, Yh_l, `pl', `hl', `matches', "`vce'", "`kernel'")
		sigmah_r = altrdvce(Xh_r, Yh_r, Yh_r, `pr', `hr', `matches', "`vce'", "`kernel'")
		sigmab_l = altrdvce(Xb_l, Yb_l, Yb_l, `pl', `hl', `matches', "`vce'", "`kernel'")
		sigmab_r = altrdvce(Xb_r, Yb_r, Yb_r, `pr', `hr', `matches', "`vce'", "`kernel'")

		invGamma_lp  = cholinv(quadcross(X_lp,wh_l,X_lp));	invGamma_rp  = cholinv(quadcross(X_rp,wh_r,X_rp))
		invGamma_lq  = cholinv(quadcross(X_lq,wb_l,X_lq));	invGamma_rq  = cholinv(quadcross(X_rq,wb_r,X_rq))
		invGammah_lp = cholinv(quadcross(Xh_lp,whh_l,Xh_lp));	invGammah_rp = cholinv(quadcross(Xh_rp,whh_r,Xh_rp))
		invGammab_lq = cholinv(quadcross(Xb_lq,wbb_l,Xb_lq));	invGammab_rq = cholinv(quadcross(Xb_rq,wbb_r,Xb_rq))

		Psih_lp = quadcross(Xh_lp, whh_l:*sigmah_l:*whh_l, Xh_lp);	Psih_rp = quadcross(Xh_rp, whh_r:*sigmah_r:*whh_r, Xh_rp)
		Psib_lq = quadcross(Xb_lq, wbb_l:*sigmab_l:*wbb_l, Xb_lq);	Psib_rq = quadcross(Xb_rq, wbb_r:*sigmab_r:*wbb_r, Xb_rq)

		factor_lp = J(pl1, 1, .)
		factor_rp = J(pr1, 1, .)
		factor_lq = J(ql1, 1, .)
		factor_rq = J(qr1, 1, .)

		for (j=1; j<=pl1; j++) {
			factor_lp[j] = factorial(j-1)
								}

		for (j=1; j<=pr1; j++) {
			factor_rp[j] = factorial(j-1)
								}

		for (j=1; j<=ql1; j++) {
			factor_lq[j] = factorial(j-1)
								}

		for (j=1; j<=qr1; j++) {
			factor_rq[j] = factorial(j-1)
								}

		Hlp_vec = J(pl1, 1, .)
		for (j=1; j<=pl1; j++) {
			Hlp_vec[j] = `hl'^(-(j-1))
								}
		Hlp = diag(Hlp_vec)

		Hrp_vec = J(pr1, 1, .)
		for (j=1; j<=pr1; j++) {
			Hrp_vec[j] = `hr'^(-(j-1))
								}
		Hrp = diag(Hrp_vec)

		Hlq_vec = J(ql1, 1, .)
		for (j=1; j<=ql1; j++) {
			Hlq_vec[j] = `bl'^(-(j-1))
								}
		Hlq = diag(Hlq_vec)

		Hrq_vec = J(qr1, 1, .)
		for (j=1; j<=qr1; j++) {
			Hrq_vec[j] = `br'^(-(j-1))
								}
		Hrq = diag(Hrq_vec)

		tau_lp = factor_lp:*(invGammah_lp*quadcross(Xh_lp, whh_l, Yh_l));	tau_rp = factor_rp:*(invGammah_rp*quadcross(Xh_rp, whh_r, Yh_r))
		tau_lq = factor_lq:*(invGammab_lq*quadcross(Xb_lq, wbb_l, Yb_l));	tau_rq = factor_rq:*(invGammab_rq*quadcross(Xb_rq, wbb_r, Yb_r))

		V_lp = invGammah_lp*Psih_lp*invGammah_lp;	V_rp = invGammah_rp*Psih_rp*invGammah_rp
		V_lq = invGammab_lq*Psib_lq*invGammab_lq;	V_rq = invGammab_rq*Psib_rq*invGammab_rq


		if (`bl'>=`hl'){
			whb_l = select(wh_l,wb_l:>0)
			Xb_lp = select(X_lp,wb_l:>0)
			Psi_lpq = quadcross(Xb_lp,whb_l:*sigmab_l:*wbb_l,Xb_lq)
						}
		else {
			wbh_l = select(wb_l,wh_l:>0)
			Xh_lq = select(X_lq,wh_l:>0)
			Psi_lpq = quadcross(Xh_lp,whh_l:*sigmah_l:*wbh_l,Xh_lq)
				}

		if (`br'>=`hr'){
			whb_r = select(wh_r,wb_r:>0)
			Xb_rp = select(X_rp,wb_r:>0)
			Psi_rpq = quadcross(Xb_rp,whb_r:*sigmab_r:*wbb_r,Xb_rq)
						}
		else {
			wbh_r = select(wb_r,wh_r:>0)
			Xh_rq = select(X_rq,wh_r:>0)
			Psi_rpq = quadcross(Xh_rp,whh_r:*sigmah_r:*wbh_r,Xh_rq)
				}

		Cov_l = invGamma_lp*Psi_lpq*invGamma_lq;	Cov_r = invGamma_rp*Psi_rpq*invGamma_rq

		v_lp = (Xh_lp:*whh_l)'*(uhh_l:^(`pl'+1));	v_rp = (Xh_rp:*whh_r)'*(uhh_r:^(`pr'+1))
		v_lq = (Xb_lq:*wbb_l)'*(ubb_l:^(`ql'+1));	v_rq = (Xb_rq:*wbb_r)'*(ubb_r:^(`qr'+1))
		v_lp1 = (Xh_lp:*whh_l)'*(uhh_l:^(`pl'+2));	v_rp1 = (Xh_rp:*whh_r)'*(uhh_r:^(`pr'+2))

		BiasConst_lp = factorial(`deriv')*cholinv(Hlp)*invGamma_lp*v_lp
		BiasConst_rp = factorial(`deriv')*cholinv(Hrp)*invGamma_rp*v_rp

		BiasConst_lp1= factorial(`deriv')*cholinv(Hlp)*invGamma_lp*v_lp1
		BiasConst_rp1= factorial(`deriv')*cholinv(Hrp)*invGamma_rp*v_rp1

		BiasConst_lq = cholinv(Hlq)*invGamma_lq*v_lq
		BiasConst_rq = cholinv(Hrq)*invGamma_rq*v_rq

		Bias_l_tau_CCT_part1 = (`hl')^(`pl'+2-`deriv')*(-m3_l*BiasConst_lp1[`deriv'+1,1])
		Bias_l_tau_CCT_part2 = (`hl')^(`pl'+1-`deriv')*(`bl'^(`ql'-`pl'))*factorial(`deriv')*(m3_l*BiasConst_lq[`pl'+2,1]*BiasConst_lp[`deriv'+1,1])

		Bias_r_tau_CCT_part1 = (`hr')^(`pr'+2-`deriv')*(m3_r*BiasConst_rp1[`deriv'+1,1])
		Bias_r_tau_CCT_part2 = (`hr')^(`pr'+1-`deriv')*(`br'^(`qr'-`pr'))*factorial(`deriv')*(m3_r*BiasConst_rq[`pr'+2,1]*BiasConst_rp[`deriv'+1,1])

		Bias_l_tau_CCT = Bias_l_tau_CCT_part1-Bias_l_tau_CCT_part2
		Bias_r_tau_CCT = Bias_r_tau_CCT_part1-Bias_r_tau_CCT_part2

		Bias_l_tau = (-tau_lq[`pl'+2,1]*BiasConst_lp[`deriv'+1,1]) * (`hl'^(`pl'+1-`deriv')/factorial(`pl'+1))
		Bias_r_tau = (tau_rq[`pr'+2,1]*BiasConst_rp[`deriv'+1,1]) * (`hr'^(`pr'+1-`deriv')/factorial(`pr'+1))

		*Bias_tau = (tau_rq[`p'+2,1]*BiasConst_rp[`deriv'+1,1] - tau_lq[`p'+2,1]*BiasConst_lp[`deriv'+1,1])*(`h'^(`p'+1-`deriv')/factorial(`p'+1))

		*tau_cl = tau_rp[`deriv'+1,1] - tau_lp[`deriv'+1,1]
		*tau_bc = tau_rp[`deriv'+1,1] - tau_lp[`deriv'+1,1] - Bias_tau

		V_l_cl = factorial(`deriv')^2*V_lp[`deriv'+1,`deriv'+1]
		V_r_cl = factorial(`deriv')^2*V_rp[`deriv'+1,`deriv'+1]
		V_l_rb = factorial(`deriv')^2*V_lp[`deriv'+1,`deriv'+1] + factorial(`pl'+1)^2*V_lq[`pl'+2,`pl'+2]*(BiasConst_lp[`deriv'+1]*`hl'^(`pl'+1-`deriv')/factorial(`pl'+1))^2 - 2*factorial(`deriv')*factorial(`pl'+1)*Cov_l[`deriv'+1,`pl'+2]*(BiasConst_lp[`deriv'+1]*`hl'^(`pl'+1-`deriv')/factorial(`pl'+1))
		V_r_rb = factorial(`deriv')^2*V_rp[`deriv'+1,`deriv'+1] + factorial(`pr'+1)^2*V_rq[`pr'+2,`pr'+2]*(BiasConst_rp[`deriv'+1]*`hr'^(`pr'+1-`deriv')/factorial(`pr'+1))^2 - 2*factorial(`deriv')*factorial(`pr'+1)*Cov_r[`deriv'+1,`pr'+2]*(BiasConst_rp[`deriv'+1]*`hr'^(`pr'+1-`deriv')/factorial(`pr'+1))
		*V_cl   = V_l_cl + V_r_cl
		*V_rb   = V_l_rb + V_r_rb

		*V_bias = factorial(`p'+1)^2*V_rq[`p'+2,`p'+2]*(BiasConst_rp[`deriv'+1]*`h'^(`p'+1-`deriv')/factorial(`p'+1))^2 + factorial(`p'+1)^2*V_lq[`p'+2,`p'+2]*(BiasConst_lp[`deriv'+1]*`h'^(`p'+1-`deriv')/factorial(`p'+1))^2

		st_numscalar("m3_l", m3_l)
		st_numscalar("m3_r", m3_r)
		st_numscalar("amse_l_cl", `scalepar'^2*(Bias_l_tau^2 + V_l_cl))
		st_numscalar("amse_r_cl", `scalepar'^2*(Bias_r_tau^2 + V_r_cl))
		st_numscalar("amse_l_bc", `scalepar'^2*(Bias_l_tau_CCT^2 + V_l_rb))
		st_numscalar("amse_r_bc", `scalepar'^2*(Bias_r_tau_CCT^2 + V_r_rb))

		}


	restore

	************************************************
	********* OUTPUT RESULT ************************
	************************************************
	di ""
	di "The estimated AMSE for the conventional left-side estimator of order `pl' is " amse_l_cl
	di "The estimated AMSE for the conventional right-side estimator of order `pr' is " amse_r_cl

	if (m3_l!=.) {
		di "The estimated AMSE for the bias-corrected left-side estimator of order `pl' is " amse_l_bc
					}
	else {
		di "Due to multicollinearity in estimating higher derivatives,"
		di "the AMSE for the bias-corrected left-side estimator of order `pl' cannot be computed."
		}

	if (m3_r!=.) {
		di "The estimated AMSE for the bias-corrected right-side estimator of order `pr' is " amse_r_bc
					}
	else {
		di "Due to multicollinearity in estimating higher derivatives,"
		di "the AMSE for the bias-corrected right-side estimator of order `pr' cannot be computed."
		}

	return scalar amse_l_cl = amse_l_cl
	return scalar amse_r_cl = amse_r_cl
	return scalar amse_l_bc = amse_l_bc
	return scalar amse_r_bc = amse_r_bc

	*mata mata clear

end