function [h,ClusterA,ClusterB]=moptxf(x,r_xx,M_h,N_h,Criterion,w,ClusterA,ClusterB,MaxNumIter,h,M_h_cluster,N_h_cluster,N_T)
% MOPTXF  Multiple optimal texture filters design 
%       [h,ac,cb]=moptxf([x_1;...;x_m],[r_xx1;...;r_xxm],M_h,N_h,Criterion,w,m)
%       clusters the m textures and designs filters for each cluster pair.
%       x_1...x_m are the texture images and r_xx1...r_xxm are their
%       autocorrelation functions (both textures and correlations must be
%       of identical size). The filters, h, are of size M_h by N_h, and 
%       designed according to Criterion (see below). All filters are stacked
%       in the matrix h. w is the smoothing filter, and m is the number of 
%       textures. The cluster pairs are returned in ca and cb.
%       
%       h=moptxf([x_1;...;x_m],[r_xx1;...;r_xxm],M_h,N_h,Criterion,w,ca,cb)
%       uses the supplied clusters ca and cb, and determines the number
%       of textures from the largest element in ca and cb.
%       
%       ...=moptxf(...cb,MaxNumIter,h) will, if a gradient search criterion
%       is selected, use the h input as initial filters, and will not iterate 
%       more than MaxNumIter.
%       
%       Supported criteria are:
%         'Unser'       Closed form 
%         'Singh'       Closed form
%         'MeanScatter' Relatively fast gradient search solution
%         'FisherG'     Complex gradient search solution

if exist('ClusterB')~=1,    ClusterB=[];     end
if exist('M_h_cluster')~=1, M_h_cluster=[];  end
if exist('N_h_cluster')~=1, N_h_cluster=[];  end
if exist('N_T')~=1,         N_T=[];          end
if isempty(M_h_cluster),    M_h_cluster=M_h; end
if isempty(N_h_cluster),    N_h_cluster=N_h; end

if isempty(N_T)
  if isempty(ClusterB)
    N_T=ClusterA;
  else
    N_T=max(max([ClusterA ClusterB]));    % Number of textures
  end
end
[M_r,N_r]=size(r_xx); M_r=M_r/N_T;      % Size of r_xxi
[M_x,N_x]=size(x);    M_x=M_x/N_T;      % Size of x_i  
if isempty(ClusterB)
  N_T=ClusterA;
  Distance=txtursim([],r_xx,N_T,Criterion,M_h,N_h);
  [ClusterA,ClusterB]=txturgroup(Distance,mean(reshape(x.',M_x*N_x,N_T))',...
    r_xx,M_h_cluster,N_h_cluster,w,'MeanSep',1.2,[],2,1);
end
N_H=size(ClusterA,1); % Number of filters to be designed

for i=1:N_H
  ClusterAA=nonzero(ClusterA(i,:));
  ClusterBB=nonzero(ClusterB(i,:));
  % Filter optimization 
  x_A=x((1:M_x)'*ones(1,length(ClusterAA)) ...
    +M_x*(ClusterAA(ones(1,M_x),:)-1),:); 
  x_B=x((1:M_x)'*ones(1,length(ClusterBB)) ....
    +M_x*(ClusterBB(ones(1,M_x),:)-1),:); 
  if  strcmp(lower(Criterion),'unser') | strcmp(lower(Criterion),'singh')
    r_xxA=eval(sprintf('+r_xx((1:M_r)+%d,:)',(ClusterAA-1)*M_r))...
      /length(ClusterAA);
    r_xxB=eval(sprintf('+r_xx((1:M_r)+%d,:)',(ClusterBB-1)*M_r))...
      /length(ClusterBB);
  else
    r_xxA=eval(sprintf('[%s]',...
      sprintf(';r_xx((1:M_r)+%d,:)',(ClusterAA-1)*M_r)));
    r_xxB=eval(sprintf('[%s]',...
      sprintf(';r_xx((1:M_r)+%d,:)',(ClusterBB-1)*M_r)));
  end
  if  strcmp(lower(Criterion),'unser') ...
      | strcmp(lower(Criterion),'singh')
    h((1:M_h)+(i-1)*M_h,1:N_h) ...
      =optxf(Criterion,x_A,x_B,[M_h N_h],w,r_xxA,r_xxB);
  elseif strcmp(lower(Criterion),'fisher') ...
      |    strcmp(lower(Criterion),'fisherg')
    % Set gradient search options
    opt=[];
    w_1=ones(5,5)/5^2;    opt(1,1)=1; opt(1,3)=1e-3; opt(1,14)=300;
    w_2=ones(9,9)/9^2;    opt(2,1)=1; opt(2,3)=1e-3; opt(2,14)=150;
    w_3=ones(13,13)/13^2; opt(3,1)=1; opt(3,3)=1e-3; opt(3,14)=75;
    opt(4,1)=1; opt(4,3)=1e-3; opt(4,14)=MaxNumIter;
    h((1:M_h)+(i-1)*M_h,:)=multifilt_gopt(h((1:M_h)+(i-1)*M_h,:),Criterion,...
      mean(reshape(x_A.',M_x*N_x,length(ClusterAA))),...
      mean(reshape(x_B.',M_x*N_x,length(ClusterBB))),...
      r_xxA,r_xxB,w,0, ...
      opt(size(opt,1),:),w_1,opt(1,:),w_2,opt(2,:),w_3,opt(3,:));
  elseif strcmp(lower(Criterion),'meanscatter') 
    if length(ClusterAA)>1 | length(ClusterBB)>1
      opt=1; opt(3)=1e-3; opt(14)=MaxNumIter;
      h((1:M_h)+(i-1)*M_h,:)=multifilt_gopt(h((1:M_h)+(i-1)*M_h,:),...
        Criterion,...
        mean(reshape(x_A.',M_x*N_x,length(ClusterAA))),...
        mean(reshape(x_B.',M_x*N_x,length(ClusterBB))),...
        r_xxA,r_xxB,w,0,opt);
    else
      % Closed form optimal two-texture filter already found
    end
  else
    error(sprintf('Criterion "%s" not supported',Criterion));
  end
  eval(sprintf('h_%d=h;',i-1));
end



