%FILTER_DESIGN_ZP_UNC Design a 2-D zero phase filter by using
%   the minimax error between the ideal frequency response and the
%   approximation given by the window method as the design criteria. UNC
%   stands for unconstrained optimization.
%
%   h = filter_design_zp_unc(Hd,Nfilt_row,Nfilt_col)
%   Hd: the ideal frequency response
%   Nfilt_row: vertical order of the output filter
%   Nfilt_col: horizontal order of the output filter
%   h: output filter
%
%   This function will also print out the minimax error between the output
%   filter and the ideal filter.
%
%   Example
%   --------
%   Design a 15x15 lowpass filter based on the ideal frequency response Hd
%
%   h=filter_design_zp_unc(Hd,15,15);
%
%   NOTE: Hd can be designed using the function ideal_response_zp. Please
%   refer to the help documents of ideal_response_zp for more information.
function h = filter_design_nq_unc(Hd,Nfilt_row,Nfilt_col)

Npt=size(Hd,1);
Hd_half = Hd((size(Hd,1)*0.5+1):size(Hd,1),:);

%Generate desired window
win=ones(Nfilt_row,Nfilt_col);
%Generate actual filter response using window method
ha = fwind2(Hd,win);
a = ha(((size(ha,1)-1)*0.5+1):size(ha,1) ,:);

%Extract vector of ind. coefficients
ha_vec0 = [reshape(a(2:size(a,1),:),size(a,1)*size(a,2)-size(a,2),1);a(1,(size(a,2)-1)/2+1:size(a,2))'];

% %Set optimizing options
% options = optimset('MaxFunEvals',5000,'TolFun',1e-10,'TolCon',1e-10,'TolX',1e-5);

%Optimize solution
p=2;
exitcond=0;
sol = fminunc(@(ha_vec) p_error(ha_vec,Hd_half,Npt,Nfilt_row,Nfilt_col,2),ha_vec0);
error_minimax=minmax_error(sol,Hd_half,Npt,Nfilt_row,Nfilt_col);
while not(exitcond)
    p=p+2;
    sol_tem = fminunc(@(ha_vec) p_error(ha_vec,Hd_half,Npt,Nfilt_row,Nfilt_col,p),ha_vec0);
    err_tem=minmax_error(sol_tem,Hd_half,Npt,Nfilt_row,Nfilt_col);
    if (err_tem>=error_minimax)
        exitcond=1;
        p-2
    else
        sol=sol_tem;
        error_minimax=err_tem;
    end
end

% %Calculate minimax error
% error_minmax = minmax_error(sol,Hd_half,Npt,Nfilt_row,Nfilt_col)
error_minimax
%Generate full response from vector of solution
tem=[fliplr(sol(Nfilt_col*(Nfilt_row-1)/2+2:length(sol))') sol(Nfilt_col*(Nfilt_row-1)/2+1:length(sol))'; 
    reshape(sol(1:Nfilt_col*(Nfilt_row-1)/2),(Nfilt_row-1)/2,Nfilt_col)];
h = [flipud(fliplr(tem(2:size(tem,1),:)));tem];


%%%%%%%%%%%%%Subfunctions to be used above%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%Subfunction1:
function err = p_error(ha_vec,Hd_half,Npt,Nfilt_row,Nfilt_col,p)

%Create the corner quadrants
%Convert vector of ind. coefficients to array
ha_mat=[fliplr(ha_vec(Nfilt_col*(Nfilt_row-1)/2+2:length(ha_vec))') ha_vec(Nfilt_col*(Nfilt_row-1)/2+1:length(ha_vec))'; 
    reshape(ha_vec(1:Nfilt_col*(Nfilt_row-1)/2),(Nfilt_row-1)/2,Nfilt_col)];
%Create the quadrants of the full response
dleft = ha_mat(:,1:(Nfilt_col-1)/2);
uright = fliplr(flipud(ha_mat(2:size(ha_mat,1),1:((Nfilt_col-1)*.5+1))));
dright = ha_mat(:,((Nfilt_col-1)*.5 + 1):Nfilt_col);
uleft = fliplr(flipud(ha_mat(2:size(ha_mat,1),((Nfilt_col-1)*.5+2):Nfilt_col)));
%Pad the actual response to take fft2
ha_pad = [dright zeros(size(dright,1),Npt-Nfilt_col) dleft;zeros(Npt-Nfilt_row,Npt);
    uright zeros(size(uright,1),Npt-Nfilt_col) uleft];
tem = fft2(ha_pad);
Ha_half = [tem(1:Npt/2,Npt/2+1:Npt) tem(1:Npt/2,1:Npt/2)];

%Calculate the p-error
err = sum(sum((Ha_half - Hd_half).^p));


%Subfunction 2:
function err = minmax_error(ha_vec,Hd_half,Npt,Nfilt_row,Nfilt_col)

%Generate full impulse response and frequency response from one quadrant.
%Convert vector of ind. coefficients into half of the full response
ha_mat=[fliplr(ha_vec(Nfilt_col*(Nfilt_row-1)/2+2:length(ha_vec))') ha_vec(Nfilt_col*(Nfilt_row-1)/2+1:length(ha_vec))'; 
    reshape(ha_vec(1:Nfilt_col*(Nfilt_row-1)/2),(Nfilt_row-1)/2,Nfilt_col)];
%Create the four quadrants of the response
dleft = ha_mat(:,1:(Nfilt_col-1)/2);
uright = fliplr(flipud(ha_mat(2:size(ha_mat,1),1:((Nfilt_col-1)*.5+1))));
dright = ha_mat(:,((Nfilt_col-1)*.5 + 1):Nfilt_col);
uleft = fliplr(flipud(ha_mat(2:size(ha_mat,1),((Nfilt_col-1)*.5+2):Nfilt_col)));
%Pad the actual response to take fft2
ha_pad = [dright zeros(size(dright,1),Npt-Nfilt_col) dleft;zeros(Npt-Nfilt_row,Npt);
    uright zeros(size(uright,1),Npt-Nfilt_col) uleft];
tem = fft2(ha_pad);
Ha_half = [tem(1:Npt/2,Npt/2+1:Npt) tem(1:Npt/2,1:Npt/2)];

%Calculate the minimax error
err = max(max(abs(Ha_half - Hd_half)));