#!/usr/bin/env python
# -*- coding: utf-8 -*-
##################################################################
#
# Copyright (c) 2023 CICV, Inc. All Rights Reserved
#
##################################################################
"""
@Authors:           zhanghaiwen(zhanghaiwen@china-icv.cn), yangzihao(yangzihao@china-icv.cn)
@Data:              2024/01/12
@Last Modified:     2024/01/12
@Summary:           Weight cal
"""

import numpy as np
from functools import reduce


def cal_weight_from_80(score_list):
    # weight process
    s_list = [80.1 if x == 80 else x for x in score_list]
    weight_list = abs((np.array(s_list) - 80) / 100)

    # normalization
    weight_list = weight_list / sum(weight_list)
    return weight_list


class AHP:
    def __init__(self, matrix):
        self.A = np.array(matrix)
        self.n = len(matrix)

    def _get_consistency_ratio(self, w_max):
        RI = [0, 0, 0.0001, 0.52, 0.89, 1.12, 1.26, 1.36,
              1.41, 1.46, 1.49, 1.52, 1.54, 1.56, 1.58, 1.59,
              1.5943, 1.6064, 1.6133, 1.6207, 1.6292]
        CI = (w_max - self.n) / (self.n - 1)
        CR = CI / RI[self.n]
        return CR

    def get_weights(self, method='eigenvalue'):
        # Check consistency of pairwise comparison matrix
        w, v = np.linalg.eig(self.A)
        w_index = np.argmax(w)
        w_max = np.real(w[w_index])
        cr = self._get_consistency_ratio(w_max)

        if cr > 0.1:
            raise ValueError('The pairwise comparison matrix is inconsistent.')

        # Normalize matrix
        line_sum = [sum(m) for m in zip(*self.A)]
        D = np.zeros((self.n, self.n))
        for i in range(self.n):
            for j in range(self.n):
                D[i][j] = self.A[i][j] / line_sum[j]

        # Calculate weights with selected method
        if method == 'arithmetic':
            weights = np.zeros(self.n)
            for i in range(self.n):
                weights[i] = np.average(D[i])
        elif method == 'geometric':
            weights = np.zeros(self.n)
            for i in range(self.n):
                weights[i] = reduce(lambda x, y: x * y, self.A[i])
                weights[i] = pow(weights[i], 1 / self.n)
            weights = [e / np.sum(weights) for e in weights]
        elif method == 'eigenvalue':
            weights = np.zeros(self.n)
            v_index = np.argmax(v)
            v_max = np.real(v[:, v_index])
            weights = [e / np.sum(v_max) for e in v_max]

        return weights
