function model = PLECOC_train(train_data,train_p_target,CodewordLength,thr,str)
%PL_ECOC [1] deals with paritial label learning problem by adapting the ECOC techniques [2]
%
%    Syntax
%
%       [Outputs,Pre_Labels,Accuracy]=PL_ECOC(train_data,train_p_target,test_data,test_target,CodewordLength,thr,str)
%
%    Description
%
%       PL_ECOC takes,
%           train_data     - An MxN array, the ith instance of training instance is stored in train_data(i,:)
%           train_p_target - A QxM array, if the jth class label is one of the partial labels for the ith training instance, then train_p_target(j,i) equals +1, otherwise train_p_target(j,i) equals 0
%           test_data      - An M2xN array, the ith instance of testing instance is stored in test_data(i,:)
%           test_target    - A QxM2 array, if the jth class label is the ground-truth label for the ith test instance, then test_target(j,i) equals 1; otherwise test_target(j,i) equals 0
%           CodewordLength - The codeword length used for ECOC coding
%           thr            - The thresholding parameter for the admissible binary training set
%           str            - The parameter string required by Libsvm [3]
%      and returns,
%           model is a structure continues following elements
%           model.CodewordLength        - The codeword length used for ECOC coding
%           model.baseModels            - A cell with QxCodewordLength elements. baseModels{j} is base model for j th column.
%           model.CodingMatrix          - A QxCodewordLength array, CodingMatrix(j,:) is the coding for j th class
%           model.perfermanceMatrix     - A QxCodewordLength array, QxCodewordLength(j,i) is the performance of CodingMatrix(j,i)
%           model.fea_num               - # features of training data

  
%
%  [1] M.-L. Zhang, F. Yu, C.-Z. Tang. Disambiguation-free partial label learning. IEEE Transactions on Knowledge and Data Engineering, in press.
%
%  [2] T. G. Dietterich, G. Bakiri. Solving multiclass learning problem via error-correcting output codes. Journal of Artificial Intelligence Research, 1995, 2(1): 263-286.
%
%  [3] C. C. Chang, C. J. Lin. LIBSVM: A library for support vector machines. ACM Transactions on Intelligent Systems and Technology, 2011, 2(3): Article 27.
%

if(nargin<4)
    error('Not enough input parameters, please check again.');
end

train_p_target=full(train_p_target);
% test_target=full(test_target);
[num_train,dim]=size(train_data);
num_class=size(train_p_target,1);

CodingMatrix=[];

max_iter=2000;
counter=0;
%%

for iter=1:max_iter
    if(mod(iter,50)==0)
        disp(['Coding matrix generation iteration: ',num2str(iter)]);
    end
    
    %tmpcode=((rand(num_class,1)>=0.5)-0.5)*2
    tmpcode=(rand(num_class,1)>=0.5);
    
    tmp_pos_idx=[];
    tmp_neg_idx=[];
    for i=1:num_train
        %           if(isempty(intersect(pl,tmp_neg)))
        if((train_p_target(:,i) & tmpcode) == train_p_target(:,i))
            tmp_pos_idx=[tmp_pos_idx,i];
        else
            %               if(isempty(intersect(pl,tmp_pos)))
            if((train_p_target(:,i) & ~tmpcode) == train_p_target(:,i))
                
                tmp_neg_idx=[tmp_neg_idx,i];
            end
        end
    end
    
    num_pos=length(tmp_pos_idx);
    num_neg=length(tmp_neg_idx);
    
    if((num_pos+num_neg>=thr)&&(num_pos>=5)&&(num_neg>=5))
        counter=counter+1;
        tr_pos_idx{counter,1}=tmp_pos_idx;
        tr_neg_idx{counter,1}=tmp_neg_idx;
        CodingMatrix=[CodingMatrix,tmpcode];
    end
    
    if(counter==CodewordLength)
        break;
    end
end
if(counter~=CodewordLength)
    disp(['The required codeword length ',num2str(CodewordLength),' not satisfied']);
    CodewordLength=counter;
    if(counter==0)
        error(['Empty coding matrix']);
    end
end
%   CodingMatrix(CodingMatrix==0)=-1;
baseModels=cell(CodewordLength,1);
%%
parfor k=1:CodewordLength
% for k=1:CodewordLength
    
    pos_inst=train_data(tr_pos_idx{k,1},:);
    neg_inst=train_data(tr_neg_idx{k,1},:);
    
    training_instance_matrix=[pos_inst;neg_inst];
    training_label_vector=[ones(length(tr_pos_idx{k,1}),1);zeros(length(tr_neg_idx{k,1}),1)];
    baseModel = svmtrain(training_label_vector, training_instance_matrix , str);
%     model = train(sparse(training_label_vector), sparse(training_instance_matrix) , str);
    baseModels{k,1}=baseModel;
end

%% initialize the perfermace matrix

perfermanceMatrix=zeros(num_class,CodewordLength);

parfor j=1:CodewordLength
% for j=1:CodewordLength
    baseModel = baseModels{j,1};
    testing_label_vector=ones(num_train,1);
    [predicted_label, accuracy, decision_values] = svmpredict(testing_label_vector, train_data, baseModel);
% [predicted_label, accuracy, decision_values] = predict(sparse(testing_label_vector), sparse(train_data), model);
% predicted_label = full(predicted_label);
    for i=1:num_class
        labelClass_i = predicted_label(train_p_target(i,:)==1,:);
        perfermanceMatrix(i,j) = sum(labelClass_i==CodingMatrix(i,j))/size(labelClass_i,1);
    end
end

% perfermanceMatrix
sumMatrix = repmat(sum(perfermanceMatrix,2),1,CodewordLength);
model.type = 'PLECOC';
model.perfermanceMatrix = perfermanceMatrix./sumMatrix;
model.baseModels = baseModels;
model.CodewordLength =CodewordLength;
model.CodingMatrix = CodingMatrix;
model.fea_num = dim;
end

