function [iHi,iLo,iCr] = findextrema(x)
%FINDEXTREMA find indices of local extrema and zero-crossings.
%   [IMAX,IMIN,ICRS] = FINDEXTREMA(X) returns the indices of local maxima
%   in IMAX, minima in IMIN and zero-crossing in ICRS for input vector X.
%
%   Example:
%       x = [0 1 0 0 -1 -1 -2 -2 1 0];
%       [i1,i2,i3] = findextrema(x)
%       % Returns:
%       %   i1 = 2 9
%       %   i2 = 8
%       %   i3 = 1 4 8 10
%
%   See also FIND.

% Siyi Deng; 05-29-2009;
% sdeng@uci.edu; UCI HNL;

%% BSD license;
% Copyright (c) 2009, Siyi Deng;
% All rights reserved.
% 
% Redistribution and use in source and binary forms, with or without 
% modification, are permitted provided that the following conditions are 
% met:
% 
%     * Redistributions of source code must retain the above copyright 
%       notice, this list of conditions and the following disclaimer.
%     * Redistributions in binary form must reproduce the above copyright 
%       notice, this list of conditions and the following disclaimer in 
%       the documentation and/or other materials provided with the distribution
%       
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
% POSSIBILITY OF SUCH DAMAGE.

%% Generate Plot for demo;
% x = [1 2 1 3 3 3 3 4 4 4 4 3 3 3 2 2 2 1 3 3 2 2 6 6 6 5 5 4 4 4 1 1 2]-3;
% figure; plot(x); set(gca,'xtick',0:numel(x)+2); ylim([-4 4])
% [iP,iT,iC] = findextrema(x),
% hold on; plot(iP,x(iP),'r*'); plot(iT,x(iT),'g*'); plot(iC,x(iC),'ks');
% legend({'Data','Peak','Trough','Zero-Crossing'},'location','NorthWest'); 

%% Code starts here;
if ~isvector(x), error('Input must be a vector;'); end
dx = diff(x);
gz = dx > 0;
lz = dx < 0;
ez = dx == 0;
hi = gz(1:end-1) & lz(2:end); 
hasCorner = any(ez);
if hasCorner
    cornerVec = double(gz(1:end-1) & ez(2:end))+...
        double(ez(1:end-1) & lz(2:end)).*2+...
        double(ez(1:end-1) & gz(2:end)).*110+...
        double(lz(1:end-1) & ez(2:end)).*100;
    cornerLoc = find(cornerVec);
    cornerType = diff(cornerVec(cornerLoc));
    n = find(cornerType == 1); % plateu;
    hi(ceil((cornerLoc(n+1)+cornerLoc(n))/2)) = true;
end
iHi = find(hi)+1;
if nargout > 1
    lo = gz(2:end) & lz(1:end-1);
    if hasCorner
        u = find(cornerType == 10); % valley;
        lo(ceil((cornerLoc(u+1)+cornerLoc(u))/2)) = true;
    end
    iLo = find(lo)+1;
end
if nargout > 2
    xc = (x(1:end-1).*x(2:end)) < 0;
    xz = false(length(x)+2,1);
    xz(2:end-1) = x == 0;
    if any(xz)
        xc(fix((find(~xz(1:end-1) & xz(2:end))+...
            find(~xz(2:end) & xz(1:end-1)))/2)) = true;
    end
    iCr = find(xc);
end

end % FINDEXTREMA;

