function w = train_llr_fusion_robust(targets, non_targets, prior, robust_weight)
%
% Revised version by Geoffrey Stewart Morrison [http://geoff-morrison.net] 2009-07-02 which handles cases of complete separation in the data.
% Mislabels one copy of 'targets' as non-targets, and gives it a weight of 'robust_weight' times the correctly labelled 'non_targets'.
% Mutatis mutandis for one copy of 'non-targets'.
% A suitable value for 'robust_weight' might be 0.001
% Default value for 'robust_weight' is 0, i.e., same as original 'train_llr_fusion' function
% 
%
% Niko Brümmer [http://niko.brummer.googlepages.com/focal]
%  Train Linear fusion with prior-weighted Logistic Regression objective.
%  The fusion output is encouraged by this objective to be a well-calibrated log-likelihood-ratio.
%  I.E., this is simultaneous fusion and calibration.
%
%  Usage:
%    w = TRAIN_LLR_FUSION(targets,non_targets,prior)
%    w = TRAIN_LLR_FUSION(targets,non_targets)
%   
%  Input parameters:
%    targets:     a [d,nt] matrix of nt target scores for each of d systems to be fused. 
%    non_targets: a [d,nn] matrix of nn non-target scores for each of the d systems.
%    prior:       (optional, default = 0.5), a scalar parameter between 0 and 1. 
%                 This weights the objective function, by replacing the effect 
%                 that the proportion nt/(nn+nt) has on the objective.
%                 For general use, omit this parameter (i.e. prior = 0.5).
%                 For NIST SRE, use: prior = effective_prior(0.01,10,1);
%
%  Output parameters:
%    w: a vector of d+1 fusion coefficients. 
%         The first d coefficients are the weights for the d input systems.
%         The last coefficient is an offset (see below).
%
%  Fusion of new scores:
%    see: LIN_FUSION.m
%                                 
%

%  This code is an adapted version of the m-file 'train_cg.m' as made available by Tom Minka
%  at http://www.stat.cmu.edu/~minka/papers/logreg/.

max_iter = 5000; %originally hard coded as 1000

if nargin < 3 || isempty(prior)
   prior = 0.5;
end


nt = size(targets,2);
nn = size(non_targets,2);

% original:
% prop = nt/(nn+nt);
% weights = [(prior/prop)*ones(1,nt),((1-prior)/(1-prop))*ones(1,nn)];
% x = [[targets;ones(1,nt)],-[non_targets;ones(1,nn)]];
% offset = logit(prior)*[ones(1,nt),-ones(1,nn)];

% make robust to complete separation
if nargin < 4 || isempty(robust_weight)
    robust_weight = 0;
end

ntnn = nt+nn;

x = [[targets, non_targets; ones(1,ntnn)],-[non_targets, targets; ones(1,ntnn)]];

robust_weight_targets = nt/nn * robust_weight;
robust_weight_non_targets = nn/nt * robust_weight;

prop_targets = nt / ntnn;
prop_non_targets = 1 - prop_targets;

prior_adjusted_targets = prior/prop_targets;
prior_adjusted_non_targets = (1-prior)/prop_non_targets;

weights = [prior_adjusted_targets*ones(1,nt), prior_adjusted_targets*robust_weight_targets*ones(1,nn), prior_adjusted_non_targets*ones(1,nn), prior_adjusted_non_targets*robust_weight_non_targets*ones(1,nt)];

offset = logit(prior)*[ones(1,ntnn),-ones(1,ntnn)];





w = zeros(size(x,1),1);

[d,n] = size(x);
old_g = zeros(size(w));
for iter = 1:max_iter
  old_w = w;
  % s1 = 1-sigma
  s1 = 1./(1+exp(w'*x+offset));
  g = x*(s1.*weights)';
  if iter == 1
    u = g;
  else
    u = cg_dir(u, g, old_g);
  end
  
  % line search along u
  ug = u'*g;
  ux = u'*x;
  a = weights.*s1.*(1-s1);
  uhu = (ux.^2)*a';
  w = w + (ug/uhu)*u;
  old_g = g;
  if max(abs(w - old_w)) < 1e-5
     break
  end
end
if iter == max_iter
  warning('not enough iters')
end
