# TITLE : Clopper-Pearson mid-P macro
# AUTHOR : André GILLIBERT (August 2017)
# LICENSE : Creative-Commons CC0
# COMPATIBILITY : Tested in Minitab 18, but may work on much older versions. Maybe down to version 9 (1993).
# SYNTAX :
# %cpmidp col;
# storecl lcl ucl;
# conf conflevel.
# 
# EXAMPLE 1:
# %cpmidp C3
# EXAMPLE 2:
# %cpmidp 'complication';
# storecl K10 K11;
# conf 0.90.
# 
# The storecl and conf subcommands are optional.
# col = column
# lcl and ucl are constants, written on output. The macro will store the lower confidence limit in lcl and the upper confidence limit un ucl
# conflevel is a numeric constant on input. It specifies the confidence level. Its default value is 0.95.
# The macro computes the Clopper-Pearson two-sided mid-P confidence interval of a binomial proportion as described in DOI:10.2307/2348891
#
# Missing values are excluded. Non-zero values are seen as successes. Zero values are seen as failures.


macro
cpmidp col;
storecl lcl ucl;
conf clevel.
mcolumn col
mconstants xx nn
mconstants lcl ucl t1 t2 clevel
mconstants succ trials lowCL uppCL
default clevel=0.95
mreset

brief 0

let xx = sum(abs(sign(col)))
let nn = n(col)

let t1=0.5-clevel/2
let t2=0.5+clevel/2
call cpmidpq xx nn t1 ucl
call cpmidpq xx nn t2 lcl

brief 2

mtitle "Clopper-Pearson mid-P two-sided Confidence-Interval";
	notitle.
let succ=xx
let trials=nn
let lowCL=lcl
let uppCL=ucl
print succ trials lowCL uppCL
endmtitle
endmacro

macro # Get the confidence limit (0.025 -> upper CL of a 95%CI and 0.0975 -> lower CL of a 95%CI. Yep, it's reversed)
# Input : xx = successes nn=trial target=0.025 or 0.975
# Output : cl=Confidence limit
cpmidpq xx nn target cl
mconstants xx nn target cl
mconstants xlower xupper xmid flower fupper fmid prec

let xlower=0
let xupper=1
let prec = 1e-8/nn

let flower = 1-target
let fupper = -target

while (xupper - xlower) > prec
	let xmid = (xupper+xlower)/2
	call cpmidpvalue xx nn xmid target fmid
	if (fmid)*(flower) > 0
		let xlower = xmid
		let flower = fmid
	else
		let xupper = xmid
		let fupper = fmid
	endif
endwhile
let cl = (xlower + xupper)/2
if target >= 0.50 and xx = 0
	let cl = 0
elseif target < 0.50 and xx = nn
	let cl = 1
endif
endmacro

macro
cpmidpvalue xx nn pp target pvalue
mconstants xx nn pp pvalue1 pvalue2 target pvalue
	cdf xx pvalue1;
		binomial nn pp.
	pdf xx pvalue2;
		binomial nn pp.
	let pvalue = pvalue1 - pvalue2/2 - target
endmacro
