from __future__ import division
import numpy as np
from adaboost.Adaboost import *

from adaboost.WeakClassifier import WeakClassifier


class AdaboostClassifier:

    # calculate new Weight
    def cal_W(self, W, alpha, y, pred):
        ret = 0
        new_W = []
        for i in range(len(y)):
            new_W.append(W[i] * np.exp(-alpha * y[i] * pred[i]))
        return np.array(new_W / sum(new_W)).reshape([len(y), 1])

    # calculate error rate per iteration
    def cal_e(self, y, pred, W):
        ret = 0
        for i in range(len(y)):
            if y[i] != pred[i]:
                ret += W[i]
        return ret

    # calculate alpha
    def cal_alpha(self, e):
        if e == 0:
            return 10000
        elif e == 0.5:
            return 0.001
        else:
            return 0.5 * np.log((1 - e) / e)

    # calculate final predict value
    def cal_final_pred(self, i, alpha, weak, y):
        ret = np.array([0.0] * len(y))
        # print("i=", i)
        for j in range(i + 1):
            ret += alpha[j] * weak[j].pred
        return np.sign(ret)

    # calculate final error rate
    def cal_final_e(self, y, cal_final_predict):
        ret = 0
        for i in range(len(y)):
            if y[i] != cal_final_predict[i]:
                ret += 1
        return ret / len(y)

    # train
    def fit(self, X, y, M=20):
        W = {}
        self.weak = {}
        self.alpha = {}
        pred = {}

        for i in range(M):
            W.setdefault(i)
            self.weak.setdefault(i)
            self.alpha.setdefault(i)
            pred.setdefault(i)

        # per iteration (all:M times)
        for i in range(M):
            # for the first iteration,initial W
            if i == 0:
                W[i] = np.array([1] * len(y)) / len(y)
                W[i] = W[i].reshape([len(y), 1])
            # if not the first iteration,calculate new Weight
            else:
                W[i] = self.cal_W(W[i - 1], self.alpha[i - 1], y, pred[i - 1])

            # using train weak learner and get this learner predict value
            self.weak[i] = WeakClassifier()
            self.weak[i].fit(X, y, W[i])
            pred[i] = self.weak[i].pred

            # calculate error rate this iteration
            e = self.cal_e(y, pred[i], W[i])
            # calculate alpha this iteration
            self.alpha[i] = self.cal_alpha(e)
            # calculate the final predict value
            cal_final_predict = self.cal_final_pred(i, self.alpha, self.weak, y)

            print('iteration:%d' % (i + 1))
            # print('self.decision_key=%s' % (self.weak[i].decision_key))
            # print('self.decision_feature=%d' % (self.weak[i].decision_feature))
            print('decision_threshold=%f' % (self.weak[i].decision_threshold))
            # print('W=%s' % (W[i]))
            # print(len(W[i]))
            print('pred=%s' % (pred[i]))
            print('e:%f alpha:%f' % (e, self.alpha[i]))
            # print('cal_final_predict:%s' % (cal_final_predict))
            print('cal_final_e:%s%%' % (self.cal_final_e(y, cal_final_predict) * 100))
            print('')

            # calculate the final error rate,if it is zero,stop iteration.
            # if self.cal_final_e(y, cal_final_predict) == 0 or e == 0:
            #     break
        # return the iteration times,from 1 on.
        # print(self.alpha)
        # self.cal_final_pred(20, self.alpha, self.weak, y)
        # print(self.weak)
        self.times = i + 1
        return self.times, self.alpha, self.weak, W[i]

