function [EOFm,PC,spec,RC]=EOF_hepta(Z,lat,thre);
% function [EOFm,PC,spec,RC]=EOF_hepta(Z,lat,thre)
%   
%   EOF analysis and synthesis of matrix Z, 
%   
%   feat.  @ area-weighting (sqrt(cos(lat))) of data matrix as in North et al 1982
%           @ PCs with unit variance . 
%           @ EOF  patterns scaled by the eigenvalue squared
%               ...  i.e. the total (area-weighted) variance they explain  . 
%            
%   in other words :  
%   EOF patterns have a physical meaning, the PCs have unit energy .
%      (this is somewhat unusual so make sure this is what you want)
%  
%     INPUTS :  - Z (nt,ny,nx) 
%      Z is assumed to contain NO NAN's . Get back to the drawing board if it does.   
%                    - lat : latitude vector  (length ny) in degrees
%                    - THRE : variance threshold for PCA reconstruction ( in %)   
%                                [default is 90%]   
%                     
%     OUTPUTS :
%                    - EOFm matrix, size nm*ny*nx (nm= number of modes retained)
%                     - PC matrix (nt*nm)
%                     - spec : (full) eigenvalue spectrum in terms of % variance explained
%                     - RC : PCA-reconstructed data matrix with nm modes 
%        
%    note : nm is such that sum(spec(1:nm)) >= THRE ;
%   
%   ref :
%   North, Gerald R., Bell, Thomas L., Cahalan, Robert F., Moeng, Fanthune J.
% "Sampling Errors in the Estimation of Empirical Orthogonal Functions"
%  Monthly Weather Review 1982 110: 699-706
%      
%   thanks to Yochanan Kushnir (LDEO) for precious advice.    
%   
%   Hepta Technologies, Aug 2006, Columbia University, New York.  
%   v1.1 : Bug in PC normalization fixed, Feb 2007, GaTech.
%   v1.2 : Now only retains nm modes (smaller matrices)     July, 25 2007, GaTech 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
%
if sum(isnan(Z)) >0
   disp('I told you not to put any NaNs in there, damnit !!')
end

%
if nargin<3
   thre=90;
end


% get sizes
[nt,ny,nx]=size(Z);
np=nx*ny; q=min(nt,np);
%%%%%% scale by area 
Zr=zeros(size(Z));
for jj=1:ny
   Zr(:,jj,:)=Z(:,jj,:)*sqrt(cos(pi/180*lat(jj)));
end

% reshape 
Zw=reshape(Zr,nt,np);
%  make sure avg is zero
for jp=1:np
   Zw(:,jp)=Zw(:,jp)-mean(Zw(:,jp))*ones(nt,1);
end


%%%%%%%%%%%% this is the intensive part %%%%%%%%%
% Determine singular value decomposition of the problem
[E,S,P]=svd(Zw',0); % works fast iff the SECOND dimension is small

% rewrite the large eigenvalue matrix to a vector and 
% apply the appropriate normalization
eigv=S.^2/(q-1);    % has q elements
%
spec=100*diag(eigv)/sum(diag(eigv)); % percentage of variance explained

% number of modes to retain
crit=abs(cumsum(spec)-thre);
r=find(crit==min(crit));  

% Normalize the EOFs
Et=E*sqrt(eigv(:,1:q)); % HERE LIES THE VARIANCE INFO, not in PC
EOFm=reshape(Et,ny,nx,q); 
EOFm=EOFm(:,:,1:r);

% Determine the EOF coefficients (Principal Components)
PC=P(:,1:r);
%
% Reconstruction using variance threshold

RC=E(:,1:r)*S(1:r,1:r)*P(:,1:r)';



% sanity check   
vz=sum(var(Zw,1));
vrc=sum(var(RC,1));
 %
RCt=reshape(RC',nt,ny,nx); 
RC=zeros(size(RCt));
%rescale by inverse of area, dim nt*ny*nx
for jj=1:ny; % north and south poles are ditched : bastard 
   if (cos(pi/180*lat(jj))==0) 
      RC(:,jj,:)=0;
   else
      RC(:,jj,:)=RCt(:,jj,:)/sqrt(cos(pi/180*lat(jj)));
   end
end

return

