% FUNCTION [ THE_FAR_FRR, EER, X_N, X_EER ] = FAR_FRR_CURVE( X, GT, NPOINTS, VERBOSE );
%
% For each of NPOINTS threshold we compute FAR and FRR using the FAR_FRR function.
% Result is stored in the corresponding column of THE_FAR_FRR.
% Threshold are defined from the data X.
%
% X:  vector of values. May contain Inf or NaN values. NaN values are ignored.
% GT: vector of binary values. 1 means "to be detected". 0 means "not to be detected".
%     GT( k ) indicates whether X( k ) should be above or below a detection threshold.
% NPOINTS: integer. Number of thresholds = number of points in the FAR/FRR curve.
% VERBOSE: binary value. Default 0.
%
% THE_FAR_FRR: 2 x NPOINTS matrix of values in the [ 0 1 ] interval.
%              Row 1 contains FAR values.
%              Row 2 contains FRR values.
% EER: scalar value. Equal Error Rate point estimated from THE_FAR_FRR.
% X_N: vector of NPOINTS values. List of the thresholds.
%
% By Guillaume Lathoud 2004 - lathoud@idiap.ch

function [ the_far_frr, eer, x_n, x_eer ] = far_frr_curve( x, gt, NPOINTS, verbose );
  
  if nargin < 3
    error( 'far_frr_curve needs at least 3 input arguments!' );
  end

  if ~exist( 'verbose', 'var' )
    verbose = 0;
  end
  
  if sum( size( x ) > 1 ) > 1
    error( 'far_frr_curve needs a vector for input argument "x" !' );
  end
  
  if sum( size( gt ) > 1 ) > 1
    error( 'far_frr_curve needs a vector for input argument "gt" !' );
  end
  
  if length( x ) ~= length( gt )
    error( 'far_frr_curve: "x" and "gt" must have same length!' );
  end
  
  
  
  if NPOINTS < 3
    error( 'far_frr_curve needs NPOINTS >= 3 !' );
  end

  usable_data = find( ~isnan( x ) );
  if isempty( usable_data )
    error( 'far_frr_curve could not find any usable data!' );
  end

  % We use the data itself to define the list of thresholds

  x_us = sort( x( usable_data ) );
  n_us = length( x_us );
  k = 1 + round( ( 0:NPOINTS-1 ) / ( NPOINTS - 1 ) * ( n_us - 1 ) );
  x_n = x_us( k );
  
  % Perturbate a bit so that thresholds don't fall exactly on data points
  
  k = round( NPOINTS / 2 );
  
  x_n( 1:k ) = x_n( 1:k ) - 1e-10;
  x_n( k+1:end ) = x_n( k+1:end ) + 1e-10;
  
  % Loop through the list of thresholds
  
  the_far_frr = repmat( NaN, 2, NPOINTS );
  
  if verbose
    chrono = chrono_start( NPOINTS );
  end
  
  for a = 1:NPOINTS
    
    threshold = x_n( a );
    
    result = x > threshold;
    
    tp = sum( result & gt );
    fp = sum( result & (~gt) );
    tn = sum( (~result) & (~gt) );
    fn = sum( (~result) & gt );
    
    [ far, frr ] = far_frr( tp, fp, tn, fn );
    
    the_far_frr( 1, a ) = far;
    the_far_frr( 2, a ) = frr;
    
    if verbose
      chrono = chrono_check( chrono, a );
    end
    
  end
  
  if verbose
    chrono_stop( chrono );
  end
  
  % Find equal error rate (EER)
  
  tmp = diff( the_far_frr( 1:2, : ), [], 1 );
  ind = find( abs( diff( sign( tmp ) ) ) ~=0 );
  
  if isempty( ind )
    error( 'far_frr_curve could not define EER!' );
  end
  
  if length( ind ) > 1
    if ~strcmp( warning, 'off' )
      disp( 'far_frr_curve: WARNING! Multiple definitions for EER!' );
    end
  end
  
  tmp = the_far_frr( 1:2, ind:ind+1 );
  eer = mean( tmp(:), 1 );
  x_eer = mean( x_n( ind:ind+1 ) );
  