*! version 8.2.2 27June2004
* Written by Peter Cummings
/* csmatch.ado */
program define csmatch, rclass
	version 8.2
	syntax varlist(min=2 max=2 numeric) [if] [in], /*
	*/ [level(real .95)] /*
	*/ Group(varname) [PERSONVar(varlist)] [PAIRVar(varlist)]
	marksample touse
	tokenize `varlist'
	tempvar outcome
	qui gen byte `outcome' = `1' if `touse'
	tempvar expos
	qui gen byte `expos' = `2' if `touse'

	/* check that outcome is coded 0/1 */
	capture assert `outcome'==0 | `outcome'==1 | `outcome'>=.
		if _rc~=0 {
			di as error "Outcome " as result "`1'" /*
			*/ as error " not coded 0/1"
			exit 450
		}

	/* check exposure is coded 0/1 */
	capture assert `expos'==0 | `expos'==1 | `expos'>=.
		if _rc~=0 {
			di as error "Exposure " as result "`2'" /*
			*/ as error " not coded 0/1"
			exit 450
		}

	/* check that groups are never greater than 2 */
	sort `group'
	tempvar grpct
	qui by `group': gen `grpct' = _N
	capture assert `grpct' <3
		if _rc~=0 {
			di as error "Some groups are greater than 2"
			exit 420
		}

	/* check that the confidence level is >0 & <1 */
	capture assert `level'>0 & `level'<1
		if _rc~=0 {
			di as error "Confidence level must be >0 and <1"
			exit 498
		}

	tempvar drivern
	qui gen byte `drivern' = 1 if `expos'==1 & `touse'
	qui replace  `drivern' = 0 if `expos'==0 & `touse'
	tempvar pass
	qui gen byte `pass' = 1 if `drivern'==0 & `touse'
	qui replace  `pass' = 0 if `drivern'==1 & `touse'
	markout `touse' `pass' `personvar' `pairvar'
	preserve
	qui keep if `touse'

	/* convert 2 record format to 1 record format */
	sort `group' `pass'
	tempvar miss1 miss2
	qui by `group': gen byte `miss1' = /*
		*/ `touse'[2] == 0 | `expos'[2]>=. | `outcome'[2]>=.
	qui by `group': gen byte `miss2' = /*
		*/ `touse'[1] == 0 | `expos'[1]>=. | `outcome'[1]>=.
	qui replace `miss1' = . if `miss1' ==1
	qui replace `miss2' = . if `miss2' ==1
	markout `touse' `miss1' `miss2'
	tempvar dout dexp
	qui by `group': gen byte `dout' = `outcome'[1] if `touse'
	qui by `group': gen byte `dexp' = `expos'[1] if `touse'
	tempvar pout pexp
	qui by `group': gen byte `pout' = `outcome'[2] if `touse'
	qui by `group': gen byte `pexp' = `expos'[2] if `touse'

	/* deal with occupant by variables. Create a temporary
	variable for each confounder in occupant list and create driver
	and passenger versions */

	local oct: word count `personvar'
	tokenize `personvar'
	local i 1
	/* generate the new occupant variables and put their names into
	local macro stro */
	local stro = ""		/* collect all the new variable names */
	local strod= ""		/* collect the new driver variable names */
	local strop= ""		/* collect the new passenger var names */
	while `i' <= `oct' {
		tempvar dobyv`i' pobyv`i'
		qui by `group': gen int `dobyv`i'' = ``i''[1] if `touse'
		qui by `group': gen int `pobyv`i'' = ``i''[2] if `touse'
		local stro = "`stro' `dobyv`i'' `pobyv`i'' "
		local strod = "`strod' `dobyv`i''"
		local strop = "`strop' `pobyv`i''"
		local i = `i' + 1
		}

	/* eliminate all records in which the two occupants are not the
	same regarding the personvar variables */
	local i 1
	tempvar nokeep
	qui gen byte `nokeep' = 1
	while `i' <= `oct' {
		qui replace `nokeep' = . if `dobyv`i'' ~= `pobyv`i''
		local i = `i' + 1
		}

	/* change 'touse' to include only the driver record */
	tempvar dronly
	qui gen byte `dronly' = 1 if `pass' ==0
	/* use only records that have at least one occupant with
	the outcome */
	* tempvar onedead
	* qui gen byte `onedead' = 1 if `pout'==1 | `dout'==1
	/* use only records that are discordant on exposure */
	tempvar oneexp
	qui gen byte `oneexp' = 1 if `pexp' ~= `dexp'
	markout `touse' `dronly' `nokeep' `oneexp'
	qui keep if `touse'
	tempvar prct
	qui count
	qui gen long `prct' = r(N)	/* count pairs available */

	/* deal with pairvar, the vehicle variables */
	local vct: word count `pairvar'

	/* GENERATE OVERALL ESTIMATE */
	/* generate the exposed to unexposed ratio */
	/* this "crude" estimate uses all pairs that are in touse,
	regardless of whether some strata are missing an estimate */
	tempvar a0 b0 c0 d0
	tempvar aaa0 bbb0 ccc0 ddd0
	tempvar rr0    /* exposed to unexposed rr */
	tempvar vlrr0  /* variance of ln rr */

	/* a b c d */
	qui gen byte `aaa0' = `dout'==1 & `pout'==1 & /*
		*/ (`dexp'==1 & `pexp'==0 /*
		*/ | `pexp'==1 & `dexp'==0)
	qui gen long `a0' = sum(`aaa0')
	qui replace `a0'  = `a0'[_N]
	qui gen byte `bbb0' = `dexp'==1 & `dout'==0 & `pexp'==0 & `pout'==1 /*
			*/ |  `pexp'==1 & `pout'==0 & `dexp'==0 & `dout'==1
	qui gen long `b0' = sum(`bbb0')
	qui replace `b0'  = `b0'[_N]
	qui gen byte `ccc0' = `dexp'==1 & `dout'==1 & `pexp'==0 & `pout'==0 /*
			*/ |  `pexp'==1 & `pout'==1 & `dexp'==0 & `dout'==0
	qui gen long `c0' = sum(`ccc0')
	qui replace `c0'  = `c0'[_N]
	qui gen byte `ddd0' = `dout'==0 & `pout'==0 & /*
		*/ (`dexp'==1 & `pexp'==0 /*
		*/ | `pexp'==1 & `dexp'==0)
	qui gen long `d0' = sum(`ddd0')
	qui replace `d0'  = `d0'[_N]

	qui gen double `rr0' = (`a0'+`c0')/(`a0'+`b0')
	qui gen double `vlrr0' = (`b0'+`c0')/[(`a0'+`b0')*(`a0'+`c0')]
	tempvar rru0
	tempvar rrl0
	qui gen double `rru0' = exp(ln(`rr0') /*
		*/ + invnorm(`level'*.5 + 0.5)*sqrt(`vlrr0'))
	qui gen double `rrl0' = exp(ln(`rr0') /*
		*/ - invnorm(`level'*.5 + 0.5)*sqrt(`vlrr0'))
	tempvar serr0 	/* standard error of rr */
	qui gen double `serr0' = exp(sqrt(`vlrr0'))

	return scalar rr = `rr0'
	return scalar vlrr = `vlrr0'
	return scalar prct = `prct'

	/* generate results by strata according
	to the personvar and pairvar variables */

	/* start a big if/else loop here. Within the first part
		calculate and display the stratum specific estimates.
		The last part will just display crude estimates. */

	if "`stro'`pairvar'" ~="" {	/* large if/else starts here */
		tempvar strata
		qui egen int `strata' = group(`stro' `pairvar')
		qui summarize `strata'
		tempvar strmax
		qui gen `strmax' = r(max)

	/* generate counts by stratum */
	/* generate the driver belted to passenger unbelted ratio */
	tempvar last
	tempvar a b c d
	tempvar aaa bbb ccc ddd
	tempvar rr    /* exposed to unexposed */
	tempvar vlrr  /* variance of ln rr */

	/* a b c d */
	sort `strata'
	tempvar last
	qui gen byte `last'=0
	qui by `strata': replace `last'=1 if _n== _N
	qui by `strata': gen byte `aaa' = `dout'==1 & `pout'==1 & /*
		*/ (`dexp'==1 & `pexp'==0 /*
		*/ | `pexp'==1 & `dexp'==0)
	qui by `strata': gen long `a' = sum(`aaa')
	qui by `strata': replace `a'  = `a'[_N]
	qui by `strata': gen byte `bbb' = `dexp'==1 & `dout'==0 & /*
			*/ `pexp'==0 & `pout'==1 /*
			*/ |  `pexp'==1 & `pout'==0 & `dexp'==0 & `dout'==1
	qui by `strata': gen long `b' = sum(`bbb')
	qui by `strata': replace `b'  = `b'[_N]
	qui by `strata': gen byte `ccc' = `dexp'==1 & `dout'==1 & /*
			*/ `pexp'==0 & `pout'==0 /*
			*/ |  `pexp'==1 & `pout'==1 & `dexp'==0 & `dout'==0
	qui by `strata': gen long `c' = sum(`ccc')
	qui by `strata': replace `c'  = `c'[_N]
	qui by `strata': gen byte `ddd' = `dout'==1 & `pout'==1 & /*
		*/ (`dexp'==1 & `pexp'==0 /*
		*/ | `pexp'==1 & `dexp'==0)
	qui by `strata': gen long `d' = sum(`ddd')
	qui by `strata': replace `d'  = `d'[_N]

	local i 1
	while `i'<=`strmax' {
		qui count if `strata' ==`i'
		tempvar prct`i'
		qui gen long prct`i' = r(N)
		return scalar prct`i' = r(N)
		local i = `i' +1
		}

	tempvar rru
	tempvar rrl
	qui keep if `last'==1
	qui gen double `rr' = (`a'+`c')/(`a'+`b')
	qui gen double `vlrr' = (`b'+`c')/[(`a'+`b')*(`a'+`c')]
	qui gen double `rru' = exp(ln(`rr') /*
		*/ + invnorm(`level'*.5 + 0.5)*sqrt(`vlrr'))
	qui gen double `rrl' = exp(ln(`rr') /*
		*/ - invnorm(`level'*.5 + 0.5)*sqrt(`vlrr'))

	/* tests of homogeneity */
	tempvar chi df pchi
	qui gen double `chi' = sum(((ln(`rr')-ln(`rr0'))^2)/`vlrr')
	qui replace `chi' = `chi'[_N]
	qui count if `rr'~=.
	qui gen int `df' = r(N) -1
	qui gen double `pchi' = chi2tail(`df',`chi')

	/* collect stratum specific scalars */
	sort `strata'
	local i 1
	while `i'<=`strmax' {
		return scalar rr`i' = `rr'[`i']
		return scalar vlrr`i' = `vlrr'[`i']
		local i = `i' +1
		}

	/* create macros for value labels for strata display */

	local names `personvar' `personvar' `pairvar'
	tokenize `names'
 	capture local lbl1 : value label `1'
	capture local lbl2 : value label `2'
	capture local lbl3 : value label `3'
	capture local lbl4 : value label `4'
	capture local lbl5 : value label `5'
	capture local lbl6 : value label `6'

	/* confine output length to 72 characters */
	di
	local c1 = 73 - (8 + length("Available pairs = "))
	local c2 = 73 - (8 + length("Pairs used = "))
	di as text "Matched-pair cohort method" _col(`c1') /*
		*/ "Available pairs = " as result %8.0g `prct'
	di as text "Stratified by: `personvar' `pairvar'"
	di as text "{hline 8}{c TT}{hline 63}"

	if `oct' ~= 0 & `vct' ~= 0 {
		local colp = 11 + 9*`oct'	/* passenger column */
		local colv = 11 + 2*9*`oct'	/* vehicle column */
		di as text _col(9)"{c |}" _col(11)"Driver" /*
			*/ _col(`colp')"Pass"  _col(`colv')"Vehicle"
	}
	else if `oct'==0 & `vct' ~= 0 {
		di as text _col(9)"{c |}" _col(11)"Vehicle"
	}
	else {
		local colp = 11 + 9*`oct'	/* passenger column */
		di as text _col(9)"{c |}" _col(11)"Driver" /*
			*/ _col(`colp')"Pass"
	}

	/* variable list will be 2*oct + 1*vct */
	tokenize `personvar' `personvar' `pairvar'
	di as text "stratum {c |}" /*
		*/ _col(11)"`1'" _col(20)"`2'" /*
		*/ _col(29)"`3'" _col(38)"`4'" /*
		*/ _col(47)"`5'" _col(56)"`6'" _col(65)"count"
	di as text "{hline 8}{c +}{hline 63}"

	local totct: word count `personvar' `personvar' `pairvar'
	tokenize `strod' `strop' `pairvar'
	sort `strata'
	if `totct' == 6 {
		label values `1' `lbl1'		/* label values */
		label values `2' `lbl2'
		label values `3' `lbl3'
		label values `4' `lbl4'
		label values `5' `lbl5'
		label values `6' `lbl6'
		local i 1
		while `i' <=`strmax' {
		if "`lbl1'" ~= "" {
			local disp1 = `1'[`i']
			local dsplbl1 : label `labl1' `disp1'
			}
		else {
			capture local dsplbl1 = `1'[`i']
		}
		if "`lbl2'" ~= "" {
			local disp2 = `2'[`i']
			local dsplbl2 : label `labl2' `disp2'
			}
		else {
			capture local dsplbl2 = `2'[`i']
		}
		if "`lbl3'" ~= "" {
			local disp3 = `3'[`i']
			local dsplbl3 : label `labl3' `disp3'
			}
		else {
			capture local dsplbl3 = `3'[`i']
		}
		if "`lbl4'" ~= "" {
			local disp4 = `4'[`i']
			local dsplbl4 : label `labl4' `disp4'
			}
		else {
			capture local dsplbl4 = `4'[`i']
		}
		if "`lbl5'" ~= "" {
			local disp5 = `5'[`i']
			local dsplbl5 : label `labl5' `disp5'
			}
		else {
			capture local dsplbl5 = `5'[`i']
		}
		if "`lbl6'" ~= "" {
			local disp6 = `6'[`i']
			local dsplbl6 : label `labl6' `disp6'
			}
		else {
			capture local dsplbl6 = `6'[`i']
		}

		di as result `stratum'[`i'] /*
			*/ as text _col(9)"{c |}" as result /*
			*/ _col(11)"`dsplbl1'" /*
			*/ _col(20)"`dsplbl2'" /*
			*/ _col(29)"`dsplbl3'" /*
			*/ _col(38)"`dsplbl4'" /*
			*/ _col(47)"`dsplbl5'" /*
			*/ _col(56)"`dsplbl6'" /*
			*/ _col(65) prct`i'[`i']
		local i = `i'+1
		}
	}
	else if `totct' == 5 {
		label values `1' `lbl1'		/* label values */
		label values `2' `lbl2'
		label values `3' `lbl3'
		label values `4' `lbl4'
		label values `5' `lbl5'
		local i 1
		while `i' <=`strmax' {
		if "`lbl1'" ~= "" {
			local disp1 = `1'[`i']
			local dsplbl1 : label `lbl1' `disp1'
			}
		else {
			capture local dsplbl1 = `1'[`i']
		}
		if "`lbl2'" ~= "" {
			local disp2 = `2'[`i']
			local dsplbl2 : label `lbl2' `disp2'
			}
		else {
			capture local dsplbl2 = `2'[`i']
		}
		if "`lbl3'" ~= "" {
			local disp3 = `3'[`i']
			local dsplbl3 : label `lbl3' `disp3'
			}
		else {
			capture local dsplbl3 = `3'[`i']
		}
		if "`lbl4'" ~= "" {
			local disp4 = `4'[`i']
			local dsplbl4 : label `lbl4' `disp4'
			}
		else {
			capture local dsplbl4 = `4'[`i']
		}
		if "`lbl5'" ~= "" {
			local disp5 = `5'[`i']
			local dsplbl5 : label `lbl5' `disp5'
			}
		else {
			capture local dsplbl5 = `5'[`i']
		}

		di as result `stratum'[`i'] /*
			*/ as text _col(9)"{c |}" as result /*
			*/ _col(11)"`dsplbl1'" /*
			*/ _col(20)"`dsplbl2'" /*
			*/ _col(29)"`dsplbl3'" /*
			*/ _col(38)"`dsplbl4'" /*
			*/ _col(47)"`dsplbl5'" /*
			*/ _col(65) prct`i'[`i']
		local i = `i'+1
		}
	}
	else if `totct' == 4 {
		label values `1' `lbl1'		/* label values */
		label values `2' `lbl2'
		label values `3' `lbl3'
		label values `4' `lbl4'
		local i 1
		while `i' <=`strmax' {
		if "`lbl1'" ~= "" {
			local disp1 = `1'[`i']
			local dsplbl1 : label `lbl1' `disp1'
			}
		else {
			capture local dsplbl1 = `1'[`i']
		}
		if "`lbl2'" ~= "" {
			local disp2 = `2'[`i']
			local dsplbl2 : label `lbl2' `disp2'
			}
		else {
			capture local dsplbl2 = `2'[`i']
		}
		if "`lbl3'" ~= "" {
			local disp3 = `3'[`i']
			local dsplbl3 : label `lbl3' `disp3'
			}
		else {
			capture local dsplbl3 = `3'[`i']
		}
		if "`lbl4'" ~= "" {
			local disp4 = `4'[`i']
			local dsplbl4 : label `lbl4' `disp4'
			}
		else {
			capture local dsplbl4 = `4'[`i']
		}

		di as result `stratum'[`i'] /*
			*/ as text _col(9)"{c |}" as result /*
			*/ _col(11)"`dsplbl1'" /*
			*/ _col(20)"`dsplbl2'" /*
			*/ _col(29)"`dsplbl3'" /*
			*/ _col(38)"`dsplbl4'" /*
			*/ _col(65) prct`i'[`i']
		local i = `i'+1
		}
	}
	else if `totct' == 3 {
		label values `1' `lbl1'		/* label values */
		label values `2' `lbl2'
		label values `3' `lbl3'
		local i 1
		while `i' <=`strmax' {
		if "`lbl1'" ~= "" {
			local disp1 = `1'[`i']
			local dsplbl1 : label `lbl1' `disp1'
			}
		else {
			capture local dsplbl1 = `1'[`i']
		}
		if "`lbl2'" ~= "" {
			local disp2 = `2'[`i']
			local dsplbl2 : label `lbl2' `disp2'
			}
		else {
			capture local dsplbl2 = `2'[`i']
		}
		if "`lbl3'" ~= "" {
			local disp3 = `3'[`i']
			local dsplbl3 : label `lbl3' `disp3'
			}
		else {
			capture local dsplbl3 = `3'[`i']
		}

		di as result `stratum'[`i'] /*
			*/ as text _col(9)"{c |}" as result /*
			*/ _col(11)"`dsplbl1'" /*
			*/ _col(20)"`dsplbl2'" /*
			*/ _col(29)"`dsplbl3'" /*
			*/ _col(65) prct`i'[`i']
		local i = `i'+1
		}
	}
	else if `totct' == 2 {
		label values `1' `lbl1'		/* label values */
		label values `2' `lbl2'
		local i 1
		while `i' <=`strmax' {
		if "`lbl1'" ~= "" {
			local disp1 = `1'[`i']
			local dsplbl1 : label `lbl1' `disp1'
			}
		else {
			capture local dsplbl1 = `1'[`i']
		}
		if "`lbl2'" ~= "" {
			local disp2 = `2'[`i']
			local dsplbl2 : label `lbl2' `disp2'
			}
		else {
			capture local dsplbl2 = `2'[`i']
		}

		di as result `stratum'[`i'] /*
			*/ as text _col(9)"{c |}" as result /*
			*/ _col(11)"`dsplbl1'" /*
			*/ _col(20)"`dsplbl2'" /*
			*/ _col(65) prct`i'[`i']
		local i = `i'+1
		}
	}
	else {
		label values `1' `lbl1'		/* label values */
		local i 1
		while `i' <=`strmax' {
		if "`lbl1'" ~= "" {
			local disp1 = `1'[`i']
			local dsplbl1 : label `lbl1' `disp1'
			}
		else {
			capture local dsplbl1 = `1'[`i']
		}

		di as result `stratum'[`i'] /*
			*/ as text _col(9)"{c |}" as result /*
			*/ _col(11)"`dsplbl1'" /*
			*/ _col(65) prct`i'[`i']
		local i = `i'+1
		}
	}


	di as text "{hline 8}{c BT}{hline 63}"
	local levpc = `level'*100
	di as text "Risk ratio estimates" /*
		*/ _col(39)"Confidence interval level = " /*
		*/ %4.1f in ye `levpc' " %"
	di as text "{hline 8}{c TT}{hline 63}"
	di as text "stratum {c |}" /*
		*/ _col(11) "E+O+/E-O+  E+O-/E-O+  E+O+/E-O- " /*
		*/ "      RR    [Conf.   Interval]"
	di as text "{hline 8}{c +}{hline 63}"
	local i 1
	while `i' <=`strmax' {
		di as result `stratum'[`i'] /*
			*/ as text _col(9)"{c |}" as result /*
			*/ _col(13) %7.0f `a'[`i'] /*
			*/ _col(24) %7.0f `b'[`i'] /*
			*/ _col(35) %7.0f `c'[`i'] /*
			*/ _col(48) %6.4f `rr'[`i'] /*
			*/ _col(55) "[" %6.4f `rrl'[`i'] "," /*
			*/ _col(66) %6.4f `rru'[`i'] "]"
		local i = `i'+1
		}
	di as text "{hline 8}{c +}{hline 63}"
	di as text "combined{c |}" as result /*
			*/ _col(48) %6.4f `rr0' /*
			*/ _col(55) "[" %6.4f `rrl0' "," /*
			*/ _col(66) %6.4f `rru0' "]"
	di as text "{hline 8}{c BT}{hline 63}"
	di as text "Homogeneity test of RR estimates " /*
		*/ as result _col(63) "p = " %5.4f `pchi'
	di as text "{hline 72}

	/* now comes the second part of the big if/else loop,
		showing results if there is only one stratum */
	}
	else 	{
		local levpc = `level'*100
		tempvar total1
		tempvar total2
		tempvar total3
		tempvar total4
		tempvar total5
		qui gen double `total1' = `a0'+`c0'
		qui gen double `total2' = `a0'+`b0'
		qui gen double `total3' = `a0'+`b0'+`c0'+`d0'
		qui gen double `total4' = `b0'+`d0'
		qui gen double `total5' = `c0'+`d0'
		di
		di as text _col(18)"{c |}{space 1}Not exposed"_col(46)"{c |}"
		di as text "Exposed" /*
			*/ _col(18) /*
			*/ "{c |}{space 3}Outcome=1{space 4}Outcome=0" /*
			*/_col(46)"{c |}{space 5}Total"
		di as text "{hline 17}{c +}{hline 27}{c +}{hline 10}"
		di as text _col(2)"Outcome = 1" /*
			*/ _col(18)"{c |}" /*
			*/ as result _col(21) %10.0f `a0' /*
			*/ _col(34) %10.0f `c0' /*
			*/ as text _col(46)"{c |}" /*
			*/ as result %10.0f `total1'
		di as text _col(2)"Outcome = 0" /*
			*/ _col(18)"{c |}" /*
			*/ as result _col(21) %10.0f `b0' /*
			*/ _col(34) %10.0f `d0' /*
			*/ as text _col(46)"{c |}" /*
			*/ as result %10.0f `total4'
		di as text "{hline 17}{c +}{hline 27}{c +}{hline 10}"
		di as text _col(10)"Total{space 3}{c |}" /*
			*/ as result _col(21) %10.0f `total2' /*
			*/ as result _col(34) %10.0f `total5' /*
			*/ as text _col(46)"{c |}" /*
			*/ as result %10.0f `total3'
		di
		di as text "Cohort matched-pair risk ratio" /*
			*/ _col(39) as text %4.1f "[" `levpc' "%" /*
			*/ " Conf. Interval]"
		di as result _col(22) %9.5f `rr0' /*
			*/ _col(39) %-9.5f `rrl0' /*
			*/ _col(50) %9.5f `rru0'

/*
	/* " _col(`c1') /*
			*/ "Available pairs = " as result %8.0g `prct'
		local levpc = `level'*100
		di as text "Risk ratio estimates" /*
		*/ _col(39)"Confidence interval level = " /*
		*/ as result %4.1f `levpc' " %" */


/* _col(40) %6.4f `rr0' /*
			*/ _col(55) as text "[" as result %6.4f `rrl0' /*
			*/ as text ", " /*
			*/ as result _col(64) %6.4f `rru0' "]" */

*/

	}

end