function [phasor_out, state] = adaptCanceller(phasor_in, state)
% adaptCanceller - adaptive noise canceller
%
% Given a sequence of measured phasors (phasor_in) and a structure that
% captures the state of the canceller, produce phasors (phasor_out) with
% the objective to make phasor_in as small as possible.
%
% The phasors phasor_in and phasor_out are represent sinusoids of a given
% frequency. They are related to the linear equation:
%
%   phasor_in = phasor_source + H*phasor_out
%
% where phasor_out is the unknown phasor of the signal to be cancelled and
% H is a 2x4 unknown complex matrix that indicates how much of a signal
% transmitted by a given speaker arrives at each microphone. Since both
% phasor_source and H are unknown, an adaptive solution is required.
%
% The input/output state can be used to carry information between
% invocations of this function. The state output in one invocation will be
% the state input in the next iteration. This can be used, for example, to
% keep track of the best phasor_out seen so far. 
%
% Inputs:
%   phasor_in - 2x1 complex vector - amplitude*exp(j*phase) for each
%               microphone
%   state - structure for maintaining information between invocations (from
%           previous invocation)
%
% Outputs:
%   phasor_out - 4x1 complex vector - amplitude*exp(j*phase) for each
%           speaker.
%   state - structure for maintaining information between invocations (for
%           next invocation)
%
% See also:
%   testCanceller


%% initialize state the first time through; 
% the first time through state == []
if isempty(state)
    state = struct( ...
        'best_pwr', Inf, ...
        'best_phasor', zeros(4,1), ...
        'last_phasor', zeros(4,1) ...
        );    
end

%% measure power of phasor_in and update state if this is current best
curr_pwr = sum(abs(phasor_in).^2);

if curr_pwr < state.best_pwr
    state.best_pwr = curr_pwr;
    
    % the phasor that produced this result is the one generated the last
    % time through!
    state.best_phasor = state.last_phasor;
end

%% adaptation: take the best current phasor and try to "tweak" it
phasor_out = state.best_phasor + 0.0005*(randn(4,1) + 1j*randn(4,1));

%% update state: record phasor_out in state
% ... in case it turns out to improve things; we won't know until next time
state.last_phasor = phasor_out;