
function liddell(varargin)
%LIDDELL: Perform Liddell's exact test on matched pairs.
% Paired proportions have traditionally been compared using McNemar's test
% but an exact alternative due to Liddell is preferable. The usual reason
% for using approximate methods, when exact methods are available, is that
% the former are "quick" if sometimes "dirty". 
% There is no such justification in the present circumstances.
% Liddell FDK. Simplified exact analysis of case-referent studies; matched
% pairs; dichotomous exposure. Journal of Epidemiology and Community Health 1983;37:82-84.
%
% Syntax: 	liddell(x,alpha)
%      
%     Inputs:
%           X - 2x2 data matrix 
%           ALPHA (default 0.05) 
%     Outputs:
%           - Point estimate of relative risk (R')
%           - Exact confidence interval
%           - F statistics with p-value
%           - Power
%
%   Example:
% In the following example, a researcher attempts to determine if a drug
% has an effect on a particular disease. 
%
%                      Drug
%                  +         -
%             --------------------
%         +   |   101   |   59   |
% Placebo     |-------------------           
%         -   |   121   |   33   |
%             --------------------
%                                       
%
%   x=[101 59; 121 33];
%
%   Calling on Matlab the function: 
%             liddell(x)
%
%   Answer is:
%
% Liddell's exact test
% Point estimate of relative risk (R') =  2.0508
% Exact 95% confidence interval = 1.4904 to 2.8493
% F = 2.0167 p-value (two-side)= 0.00000443
% alpha = 0.0500  Zb = 2.7566  Power (2-tails) = 0.0058
%
%           Created by Giuseppe Cardillo
%           giuseppe.cardillo-edta@poste.it
%
% To cite this file, this would be an appropriate format:
% Cardillo G. (2008) Liddells test: Perform Liddell's exact test on
% matched pairs
% http://www.mathworks.com/matlabcentral/fileexchange/22024


%Input error handling
args=cell(varargin);
nu=numel(args);
if isempty(nu)
    error('Warning: Matrix of data is missed...')
elseif nu>2
    error('Warning: Max two input data are required')
end
default.values = {[],0.05};
default.values(1:nu) = args;
[x alpha] = deal(default.values{:});
%check if x is a 2x2 matrix
if ~isequal(size(x),[2 2])
    error('Input matrix must be a 2x2 matrix')
end
if ~all(isfinite(x(:))) || ~all(isnumeric(x(:)))
    error('Warning: all X values must be numeric and finite')
end
if ~isequal(x(:),round(x(:)))
    error('Warning: X data matrix values must be whole numbers')
end
if nu==2 %if necessary check alpha
    if ~isscalar(alpha) || ~isnumeric(alpha) || ~isfinite(alpha) || isempty(alpha)
        error('Warning: it is required a numeric, finite and scalar ALPHA value.');
    end
    if alpha <= 0 || alpha >= 1 %check if alpha is between 0 and 1
        error('Warning: ALPHA must be comprised between 0 and 1.')
    end
end
clear args default nu

%observed subjects with only one reaction
ob=diag(fliplr(x));
if ob(1)<ob(2)
    ob=flipud(ob);
end
r=ob(1); s=ob(2);
%perform liddell's test
%R=Maximum likelihood estimate of relative risk R as shown by Mantel &
%Haenszel. Statistical aspects of the analysis of data from retrospective
%studies of disease. J Natl Cancer Inst 1959; 22:719-748.
R=r/s;
%Confidence interval of R
P=1-alpha/2;
Ru=(r+1)/s*finv(P,2*(r+1),2*s);
Rl=r/(s+1)/finv(P,2*(s+1),2*r);
%Test of the hypothesis that R=1
F=r/(s+1);
pvalue=2*(1-fcdf(F,2*(s+1),2*r));

%Compute power
Za=abs(-realsqrt(2)*erfcinv(alpha));
N=sum(x(:));
p=min(ob./N);
pp=max(ob(1)/ob(2),ob(2)/ob(1));
num=abs(realsqrt(N*p*(pp-1)^2)-realsqrt(Za^2*(pp+1)));
denom=realsqrt(pp+1-p*(pp-1)^2);
Zb=num/denom;
pwr=(1-0.5*erfc(-Zb/realsqrt(2)))*2;

%display results
disp('Liddell''s exact test')
fprintf('Maximum likelihood estimate of relative risk =  %0.4f\n',R)
fprintf('Exact %i%% confidence interval = %0.4f to %0.4f\n',(1-alpha)*100,Rl,Ru)
fprintf('F = %0.4f p-value (two-side)= %0.8f\n',F,pvalue)
fprintf('alpha = %0.4f  Zb = %0.4f  Power (2-tails) = %0.4f\n',alpha,Zb,pwr)
end

