% TAG_UNSURE_DECISIONS - tag unsure decisions for a two-class classifier
%
%     PC=TAG_UNSURE_DECISIONS(PCLF,TARGET_NAME,TARGET_ERR,NONTARGET_NAME,NONTARGET_ERR,PROCESS_UNSURE)
%
% INPUT
%    PCLF - trained pipeline containing ROC
%    TARGET_NAME - string name of target classes
%    TARGET_ERR - allowed error on true target samples
%    NONTARGET_NAME - string name of non-target class
%    NONTARGET_ERR - allowed error on true non-targets
%    PROCESS_UNSURE - optional flag if unsure decisions should be separated
%                     further by PCLF classifier (default: 0 == no)
%
% OUTPUT
%    PC - pipeline returning 'sure-TARGET','sure-NONTARGET' and 'unsure' decisions
%
% DESCRIPTION
% This function takes a trained two-class classifier pipeline with ROC with
% max.allowed error specs and outputs a pipeline that returns sure/unsure
% decisions. 
% This is done by fixing two different operating points. One, where max
% TARGETERR error is allowed on the target class and another with analogous
% solution for non-target class. Both are then combined into one pipeline
% by cascading.  The assumption is that the pipeline PCLF contains an
% estimated ROC characteristic that contains TPr and FPr measures for the
% target class.
% If the PROCESS_UNSURE flag 1 is provided, also 'unsure' decisions will be
% further divided into 'unsure-TARGET' and 'unsure-NONTARGET' by the
% original PCLF classifier.
%
% EXAMPLE
% For a apple/banana problem, we make sure the final classifier yields
% 'sure-banana' without errors, 'sure-apple' with max.1% error and rest of
% samples as 'unsure':
% >> p=sdparzen(tr)
% >> r=sdroc(val,p,'measures',{'TPr','apple','FPr','apple'},'confmat')
% >> pc=tag_unsure_decisions(p,'apple',0.01,'banana',0)

function pc=tag_unsure_decisions(pclf,targetname,targeterr,nontargetname,nontargeterr,processunsure)
    
    r=pclf.roc;
    if isempty(r)
	error('Classifier must contain ROC');
    end
    if ~exist('processunsure','var')
	processunsure=0;
    end
    
    % search for TPr(target)
    tprname=sprintf('TPr(%s)',targetname);
    out=r(:,tprname);
    if isempty(out)
	error(['Classifier ROC must contain ''' tprname ''' measure.']);
    end
    
    % search for FPr(target)
    fprname=sprintf('FPr(%s)',targetname);
    out=r(:,fprname);
    if isempty(out)
	error(['Classifier ROC must contain ''' fprname ''' measure.']);
    end
    
    % model without decision step
    pmodel=-pclf;
    
    % setup first classifier: sure target
    r1=pclf.roc;

    r1=setcurop(r1,'constrain',fprname,nontargeterr,'max',tprname);
    if isempty(r1)
	error('No operating points found to fulfill the requirements, increase max allowed non-target error');
    end
    % turn ROC into decision pipeline
    pd1=sddecide(r1);
    % rename decisions adding 'sure-' prefix to the target
    pr1=sdrelab(pd1,{targetname,['sure-' targetname]},'nodisplay');
    
    % setup second classifier: sure nontarget
    r2=setcurop(pclf.roc,'constrain',tprname,1-targeterr,'min',fprname);
    if isempty(r1)
	error('No operating points found to fulfill the requirements, increase max allowed target error');
    end
    % turn ROC into decision pipeline
    pd2=sddecide(r2);
    % rename decisions
    pr2=sdrelab(pd2,{nontargetname,['sure-' nontargetname],targetname,'unsure'},'nodisplay');

    if processunsure
	% define unsure decisions (op.point in the ROC of original classifier)
	punsure=sddecide(pclf.roc);
	% relabel decisions (add 'unsure-' prefix)
	punsure=sdrelab(punsure,'prefix','unsure-');
	% compose the final cascade
	pc=pmodel*sdcascade(pr1,nontargetname,pr2,'unsure',punsure);
    else
	% compose the final cascade
	pc=pmodel*sdcascade(pr1,nontargetname,pr2);
    end
    
    return