function [pvalue] = pvt_alpha(n,N,S,METH, CLOCK_MAX, MC_ITER)
% pvt_alpha(n,N,s) -- Computes the p-value of the Poisson variability test.
%	Parameters
%	  1) n = number of trials
%	  2) N = total number of spikes (across all trials)
%	  3) S = sum of squares (across all trials)
%	  4) METH = method of computation
%	    Possible values:
%		  0 = AUTOMATIC (Default)
%		  1 = Dynamic Programming (This is an exact p-value)
%		  2 = Monte Carlo (Approximate p-value)
%		  The AUTOMATIC method tries to compute the p-value by dynamic programming
%		  until CLOCK_MAX seconds elapses, at which point the p-value is computed by MC 
% 		  approximation
%	  5) CLOCK_MAX = maximum number of seconds to solve by dynamic programming
%		  Default value = 20 seconds
%	  6) MC_ITER = number of Monte Carlo samples to use if computed by Monte Carlo
%		  Default value = 10,000
% Two computational methods are available. Dynamic Programming produces an exact p-value but
% can be inefficient if N or S are too large. Monte Carlo samples produce an approximate
% p-value efficiently; the accuracy of the approximation can be increased arbitrarily
% by increasing MC_ITER, the number of Monte Carlo samples.
%
% Only the first three parameters are required.
%
% by Asohan Amarasingham; send comments to asohan@dam.brown.edu

if isempty(who('METH')), METH=0; end
if isempty(who('CLOCK_MAX')), CLOCK_MAX=20; end
if isempty(who('MC_ITER')), MC_ITER=10000; end

if METH==2, ['Monte Carlo'], pvalue=pvtalpha_mc(n,N,S,MC_ITER); return, end
	
if n==1      % if n=1 then the sum of squares=N (w.p.1)
    if S>=N
        prob=1;
    else
        prob=0;
    end
    return
end

if n==2     % if n=2, compute from binomial probability
    
    % for n=2, note x_1^2 + x_2^2 <= k is equivalent to   n/2-k' <= x_1 <= n/2+k', for some k'
    % i.e. find smallest integer c s.t. (x+c)^2 + (N- (x+c) )^2 > S
    x=N/2;
    if round(N/2)~=N/2, j=.5; else, j=0, end
    for c=0:ceil(1+N/2)
        if (x+j+c)^2 + (N - (x+j+c))^2 > S
            break
        end
    end
    
    if c==0
        prob=0;
    else
        c=c-1;
        leftprob=binocdf(x+c+j,N,.5);
        if x-c-j-1<0
            rightprob=0;
        else
            rightprob=binocdf(x-c-j-1,N,.5);        
        end        
        prob=leftprob-rightprob;
    end
    return
    
end
        
% initial loop..

alpha=sparse(N+1,S+1);  % index is shifted by 1 to accomodate 0 indices..

tic

for s1=0:N
    if s1^2 > S
        break
    else
        for s2=s1:N
            if s1^2 + (s2-s1)^2 > S  % ie if t2>S
                break
            else
                t2=s1^2 + (s2-s1)^2;
                alpha(s2+1,t2+1) = alpha(s2+1,t2+1) + coeff( s2,s1,1/n,s2 );
            end
        end
    end
	if toc>CLOCK_MAX, ['Monte Carlo'], pvalue=pvtalpha_mc(n,N,S,MC_ITER); return, end
end

% induction loop

for t=2:n-2

     
	beta=sparse(N+1,S+1);  % again, index shifted by 1.. beta matrix

	                           
	% find non-empty values of alpha matrix..
	% notation in mind: (sm,tm) is (s_m,t_m).. (sm1,tm1) is (s_{n+1},t_{n+1})..
	[i,j]=find(alpha);
	for cc=1:length(i)
        sm=i(cc)-1; tm=j(cc)-1;         % remember the index shift..
        for sm1=sm:N
            tm1=tm + (sm1-sm)^2;
            if tm1>S
                break
            else
                beta( sm1+1,tm1+1 ) = beta(sm1+1,tm1+1) + coeff(sm1,sm,1/n,sm1-sm)*alpha( i(cc),j(cc) );

            end
        end
		if toc>CLOCK_MAX, ['Monte Carlo'], pvalue=pvtalpha_mc(n,N,S,MC_ITER); return, end
	end

    alpha=beta;

    
end

% final loop
beta=sparse(N+1,S+1);
[i,j]=find(alpha);
for cc=1:length(i)
    sm=i(cc)-1; tm=j(cc)-1;         % remember the index shift..
    tm1=tm + (N-sm)^2;
    if tm1 <= S
        beta( N+1,tm1+1 )= beta(N+1,tm1+1) + coeff(N,sm,1/n,N-sm)*alpha( i(cc),j(cc) );
     
    end
end


% sum out t_m and you're done
pvalue=sum(sum(beta));
pvalue=full(pvalue);


return

%--------------------------------

function val = coeff(N,k,p,P)
% computes the coefficient nchoosek(N,k)*(p)^P..
% (using log gamma for more efficient numerics..)
% use gammaln to keep numbers of same order, then exponentiate..
val = exp( gammaln(N+1)-gammaln(k+1)-gammaln(N-k+1) + P*log(p) );
return

%---------------------------------
function [pvalue] = pvtalpha_mc(n,N,S,MC_ITER) 
% Returns the p-value of the Poisson variability test
% by Monte Carlo approximation (using MC_ITER),
% where n is the number of trials, N is the total number of spikes (across trials)
% and S is the sum of squares of the spike counts across trials (see text)

h=hist( ceil( unifrnd(0,1,N,MC_ITER)*n ), 1:n );   % generate multinomial samples
i=sum(h.*h);  									 % compute sum of squares
pvalue=(sum(i<=S)+1)/(MC_ITER+1);							 
% output pvalue as proportion of samples with sum of squares <= k

return


    


        
