% adaboost 
function [trerr, tserr] = adaboost(type, fea_tr, lab_tr, fea_ts, lab_ts, M)
% perform adaboost algorithm 
% type:                 the based classifier
% fea_tr, lab_tr:       training feature and label
% fea_ts, lab_ts:       testing feature and label
% M:                    iteration num
% 
% trerr                 training error (combined)
% tserr                 testing error

% first get parameters
vsize = size(fea_tr, 1);
trnum = size(fea_tr, 2);
tsnum = size(fea_ts, 2);

if (vsize ~= size(fea_ts, 1))
    disp('training feature and testing feature donnot have same size\n');
    return;
end

if (trnum ~= size(lab_tr, 2))
    disp('training set has different feature and label size\n');
    return;
end

if (tsnum ~= size(lab_ts, 2))
    disp('testing set has different feature and label size\n');
    return;
end

% deal with different base classifiers
switch lower(type)
    
case 'nearest'                      % nearest neighbour
    % parameters
    weight = ones(1, trnum) / trnum;
    alpha_M = zeros(1, M);
    hypo_tr_M = zeros(1, trnum, M);
    hypo_ts_M = zeros(1, tsnum, M);
    m = 1; err = -1;
    
    while m<=M & err<0.5 & err~=0
        % resample data
        [fea_w, lab_w, idx_w] = resample(fea_tr, lab_tr, weight);
        
        % train base classifier and run base classifier on ORIGINAL trainning data, I==1 means wrongly classified
        % for KNN, it requires only one step
        [I_tr, hypo_tr] = knn(fea_w, lab_w, fea_tr, lab_tr);        % train on training set
        [I_ts, hypo_ts] = knn(fea_w, lab_w, fea_ts, lab_ts);        % train on testing set
        
        % calculate err
        err = sum(weight .* I_tr, 2) / sum(weight, 2);
        
        % calculate alpha
        alpha_M(m) = 0.5*log((1-err)/err);
        
        % update weight
        weight = weight .* exp(2*alpha_M(m)*I_tr);
        weight = weight ./ sum(weight,2);
        
        % store parameters for round m
        hypo_tr_M(:, :, m) = hypo_tr;
        hypo_ts_M(:, :, m) = hypo_ts;
        
        % update m
        m = m + 1;
        
    end

    % show debug information
    disp(m);
    disp(alpha_M);
    
    % combine all weak learners and output trainning error
    tr_res = zeros(1, trnum, m-1);
    for i=1:m-1
        tr_res(:, :, i) = hypo_tr_M(:, :, i) * alpha_M(i);
    end
    tr_res = sign(reshape(sum(tr_res, 3), 1, trnum));
    tr_err = (tr_res ~= lab_tr);
    trerr = sum(tr_err);
    fprintf(1, 'training err of 1NN is %f\n', trerr/trnum);
    
    % combine all weak learners and output testing error
    ts_res = zeros(1, tsnum, m-1);
    for i=1:m-1
        ts_res(:, :, i) = hypo_ts_M(:, :, i) * alpha_M(i);
    end
    ts_res = sign(reshape(sum(ts_res, 3), 1, tsnum));
    ts_err = (ts_res ~= lab_ts);
    tserr = sum(ts_err);
    fprintf(1, 'testing err of 1NN is %f\n', tserr/tsnum);
    
case 'lda'
    % parameters
    weight = ones(1, trnum) / trnum;
    alpha_M = zeros(1, M);
    hypo_tr_M = zeros(1, trnum, M);
    hypo_ts_M = zeros(1, tsnum, M);
    m = 1; err = -1;
    
    while m<=M & err<0.5 & err~=0
        % resample data
        [fea_w, lab_w, idx_w] = resample(fea_tr, lab_tr, weight);
        
        % train lda and get hypo
        [w, w0] = lda_tr(fea_w, lab_w);
        [I_tr, hypo_tr] = lda_ts(fea_tr, lab_tr, w, w0);
        [I_ts, hypo_ts] = lda_ts(fea_ts, lab_ts, w, w0);
        
        % calculate err
        err = sum(weight .* I_tr, 2) / sum(weight, 2);
        
        % calculate alpha
        alpha_M(m) = 0.5*log((1-err)/err);
        
        % update weight
        weight = weight .* exp(2*alpha_M(m)*I_tr);
        weight = weight ./ sum(weight,2);
        
        % store parameters for round m
        hypo_tr_M(:, :, m) = hypo_tr;
        hypo_ts_M(:, :, m) = hypo_ts;
        
        % update m
        m = m + 1;
    end

    % show debug info
    disp(m);
    disp(alpha_M);
    
    % combine all weak learners and output trainning error
    tr_res = zeros(1, trnum, m-1);
    for i=1:m-1
        tr_res(:, :, i) = hypo_tr_M(:, :, i) * alpha_M(i);
    end
    tr_res = sign(reshape(sum(tr_res, 3), 1, trnum));
    tr_err = (tr_res ~= lab_tr);
    trerr = sum(tr_err);
    fprintf(1, 'training err of LDA is %f\n', trerr/trnum);
    
    % combine all weak learners and output testing error
    ts_res = zeros(1, tsnum, m-1);
    for i=1:m-1
        ts_res(:, :, i) = hypo_ts_M(:, :, i) * alpha_M(i);
    end
    ts_res = sign(reshape(sum(ts_res, 3), 1, tsnum));
    ts_err = (ts_res ~= lab_ts);
    tserr = sum(ts_err);
    fprintf(1, 'testing err of LDA is %f\n', tserr/tsnum);

    
case 'naivebayes'
    % parameters
    weight = ones(1, trnum) / trnum;
    alpha_M = zeros(1, M);
    hypo_tr_M = zeros(1, trnum, M);
    hypo_ts_M = zeros(1, tsnum, M);
    m = 1; err = -1;
    
    while m<=M & err<0.5 & err~=0
        % resample data
        [fea_w, lab_w, idx_w] = resample(fea_tr, lab_tr, weight);
        
        % train naive bayes and get hypo
        [mean, var, pri] = nb_tr(fea_w, lab_w);
        [I_tr, hypo_tr] = nb_ts(fea_tr, lab_tr, mean, var, pri);
        [I_ts, hypo_ts] = nb_ts(fea_ts, lab_ts, mean, var, pri);        
        
        % calculate err
        err = sum(weight .* I_tr, 2) / sum(weight, 2);
        
        % calculate alpha
        alpha_M(m) = 0.5*log((1-err)/err);
        
        % update weight
        weight = weight .* exp(2*alpha_M(m)*I_tr);
        weight = weight ./ sum(weight,2);
        
        % store parameters for round m
        hypo_tr_M(:, :, m) = hypo_tr;
        hypo_ts_M(:, :, m) = hypo_ts;
        
        % update m
        m = m + 1;
        
    end

    % show debug information
    disp(m);
    disp(alpha_M);
    
    % combine all weak learners and output trainning error
    tr_res = zeros(1, trnum, m-1);
    for i=1:m-1
        tr_res(:, :, i) = hypo_tr_M(:, :, i) * alpha_M(i);
    end
    tr_res = sign(reshape(sum(tr_res, 3), 1, trnum));
    tr_err = (tr_res ~= lab_tr);
    trerr = sum(tr_err);
    fprintf(1, 'training err of NaiveBayes is %f\n', trerr/trnum);
    
    % combine all weak learners and output testing error
    ts_res = zeros(1, tsnum, m-1);
    for i=1:m-1
        ts_res(:, :, i) = hypo_ts_M(:, :, i) * alpha_M(i);
    end
    ts_res = sign(reshape(sum(ts_res, 3), 1, tsnum));
    ts_err = (ts_res ~= lab_ts);
    tserr = sum(ts_err);
    fprintf(1, 'testing err of NaiveBayes is %f\n', tserr/tsnum);

otherwise
    disp('unknow method');
        
end
