# -*- coding: utf-8 -*-


class LPOM(object):
    """A Class calculating averaged height according to the LPOM model.

    Three variants:

    LPOM0:
      with the resulting variable self.hav LPOMbalance with the
      resulting variable self.hprob and LPOMext with the resulting
      variable self.havext

      Basis for all three variants is the concept of sets, generated by
      a single object (therefore "local partial order model".

    LPOM0:
      is the only important set Incomp(x), the set of all
      objects incomparable with x In LPOMbalance the relevant local sets
      are the down(set) and the up(set) generated by x In

    LPOMext:
      all three sets are important.

    """

    def __init__(self, precision=2):
        self.hav = []
        self.precision = precision

    def calc_hav(self, downset, incompset, rred):
        """ calculates the averaged height according to the LPOM0 model.

        :param downset: elements in the principal downset
                        of repres. elements
        :type downset: list

        :param incompset: elements incomparable
                          with a certain element
        :type incompset: list

        :param rred: representative elements
        :type rred: number

        :returns: evaeraged height
        :rtype: list

        """

        self.hav = []
        for i in range(0, rred):
            d = len(downset[i])
            lic = len(incompset[i])
            denom = 1.0 * rred + 1.0 - 1.0 * lic
            self.hav.append(
                round(1.0 * d * (rred + 1) / denom,
                      self.precision))
        return self.hav

    def calc_hav_balance(self, matrix, csv, downset, upset, rred):
        """ calculates average height by a simple balance concept

        called "LPOMbalance". The idea: estimate the mutual probability,
        mutprob(x,y) that an object x > object y, based on the set of all
        linear extensions.

        mutprob(x,x) is set 1.

        hprob(i), the average height of the ith object, due to
        LPOMbalance = sum mutprob(i,j), summing over j.

        :param downset: elements in the downsets
                        of repres. elements
        :type downset: list

        :param upset: elements in the upsets
                      of repres. elements
        :type upset: list

        :param rred: representative elements
        :type rred: list

        :returns:  average height by a simple balance concept
        :rtype: list

        """

        mutprob = []
        self.hprob = []
        Q = []
        for i in range(0, rred):
            Q.append(1.0 * len(downset[i]) / (1.0 * len(upset[i])))

        for i1 in range(0, rred):
            mutprob.append(0)
            mutprob[i1] = []
            for i2 in range(0, rred):
                denomQ = 1.0 * Q[i1] + 1.0 * Q[i2]
                if i1 == i2:
                    mutprob[i1].append(1.0)
                else:
                    mutprob[i1].append(1.0 * Q[i1] / denomQ)

        for i1 in range(0, rred):
            self.hprob.append(0)
            for i2 in range(0, rred):
                self.hprob[i1] += mutprob[i1][i2]

        return self.hprob

    def calc_hav_ext(self, downset, upset, incompset, rred):
        """ calculates the averaged height according to the LPOMext model.

        :param downset: elements in the downsets
                        of repres. elements
        :type downset: list

        :param upset: elements in the upsets
                      of repres. elements
        :type upset: list

        :param incompset: elements incomparable
                          with each repres. element
        :type incompset: list
        :param rred: representative elements
        :type rred: list

        :var upset: elements in the principal upset
                    of repres. elements
        :type upset: list

        :var pdown: counts the elements in downset
        :type pdown: int

        :var pup: counts the elements in upset
        :type pupp: int

        :returns: averaged height according to the LPOMext model
        :rtype: list

        """
        self.havext = []
        for i in range(0, rred):
            self.havext.append(len(downset[i]))
        for i1 in range(0, rred):
            inc = 0
            for i2 in incompset[i1]:
                pdown = 0
                pup = 0
                for i3 in incompset[i2]:
                    if i3 in downset[i1]:
                        pdown += 1
                for i3 in incompset[i2]:
                    if i3 in upset[i1]:
                        pup += 1
                if (pdown + pup) != 0:
                    inc += 1.0 * pdown / (1.0 * pdown + 1.0 * pup)
                else:
                    inc += 0
            self.havext[i1] += inc
        for i1 in range(0, rred):
            self.havext[i1] = round(self.havext[i1], self.precision)
        return self.havext
