def test_calculate(self):
        rewards = []
        ratios = [0.25, 0.05, 0.3, 0.15, 0.25]
        total_reward = 1000

        for i, ratio in enumerate(ratios, start=1):
            rl0 = RewardLog(
                address="addr" + str(i),
                type="D",
                staking_balance=total_reward * ratio,
                current_balance=0,
            )
            rl0.ratio = ratio
            rl0.ratio3 = ratio
            rewards.append(rl0)

        rewards[0].type = TYPE_OWNERS_PARENT
        rewards[1].type = TYPE_FOUNDERS_PARENT

        rewards.append(
            RewardLog("addrdummy", "D", 0, 0).skip("skipped for testing", 3))

        founders_map = {"addr1": 0.4, "addr2": 0.6}
        owners_map = {"addr1": 0.6, "addr2": 0.4}

        phase4 = CalculatePhase4(founders_map, owners_map)
        new_rewards, new_total_reward = phase4.calculate(rewards, total_reward)

        # new_total_reward = total_reward
        self.assertEqual(total_reward, new_total_reward)

        # check new ratios sum up to 1
        # old and new reward amount is the same
        ratio_sum = 0.0

        # filter out skipped records
        new_rewards = list(rl for rl in new_rewards if not rl.skipped)

        # 2 owner, 2 founders and 3 delegators
        self.assertEqual(7, len(new_rewards))

        founder_ratio = 0.0
        owner_ratio = 0.0
        for rl4 in new_rewards:
            if rl4.skipped:
                continue

            if rl4.type == TYPE_FOUNDER:
                founder_ratio += rl4.ratio4
            if rl4.type == TYPE_OWNER:
                owner_ratio += rl4.ratio4

            ratio_sum += rl4.ratio4

        self.assertAlmostEqual(1.0, ratio_sum, delta=1e-6)
        self.assertAlmostEqual(0.25, owner_ratio, delta=1e-6)
        self.assertAlmostEqual(0.05, founder_ratio, delta=1e-6)
    def calculate(self, reward_provider_model):

        phase0 = CalculatePhase0(reward_provider_model)
        rwrd_logs = phase0.calculate()

        total_rwrd_amnt = reward_provider_model.computed_reward_amount
        logger.info("Total rewards before processing is {:,} mutez.".format(
            total_rwrd_amnt))
        if total_rwrd_amnt == 0:
            logger.debug("NO REWARDS to process!")
            return [], 0

        assert reward_provider_model.delegate_staking_balance == sum(
            [rl.staking_balance for rl in rwrd_logs])
        assert self.almost_equal(1, sum([rl.ratio for rl in rwrd_logs]))

        # calculate phase 1
        phase1 = CalculatePhase1(self.rules_model.exclusion_set1,
                                 self.min_delegation_amnt)
        rwrd_logs, total_rwrd_amnt = phase1.calculate(rwrd_logs,
                                                      total_rwrd_amnt)

        assert self.almost_equal(
            1, sum([rl.ratio for rl in rwrd_logs if not rl.skipped]))

        # calculate phase 2
        phase2 = CalculatePhase2(self.rules_model.exclusion_set2,
                                 self.min_delegation_amnt)
        rwrd_logs, total_rwrd_amnt = phase2.calculate(rwrd_logs,
                                                      total_rwrd_amnt)

        assert self.almost_equal(
            1, sum([rl.ratio for rl in rwrd_logs if not rl.skipped]))

        # calculate phase 3
        phase3 = CalculatePhase3(self.fee_calc,
                                 self.rules_model.exclusion_set3,
                                 self.min_delegation_amnt)
        rwrd_logs, total_rwrd_amnt = phase3.calculate(rwrd_logs,
                                                      total_rwrd_amnt)

        assert self.almost_equal(
            1, sum([rl.ratio for rl in rwrd_logs if not rl.skipped]))

        founder_parent = next(
            filter(lambda x: x.type == TYPE_FOUNDERS_PARENT, rwrd_logs), None)

        calculated_founder_rewards = sum([
            rl.ratio2 for rl in rwrd_logs if rl.skippedatphase == 3
        ]) + sum([rl.service_fee_ratio for rl in rwrd_logs if not rl.skipped])

        if founder_parent:
            assert self.almost_equal(founder_parent.ratio3,
                                     calculated_founder_rewards)
        else:
            assert self.almost_equal(0, calculated_founder_rewards)

        owners_parent = next(
            filter(lambda x: x.type == TYPE_OWNERS_PARENT, rwrd_logs), None)
        if owners_parent:
            assert owners_parent.service_fee_rate == 0

        phase4 = CalculatePhase4(self.founders_map, self.owners_map)
        rwrd_logs, total_rwrd_amnt = phase4.calculate(rwrd_logs,
                                                      total_rwrd_amnt)

        # calculate amounts
        phase_last = CalculatePhaseFinal()
        rwrd_logs, total_rwrd_amnt = phase_last.calculate(
            rwrd_logs, total_rwrd_amnt)

        # sort rewards according to type and balance
        rwrd_logs.sort(key=functools.cmp_to_key(cmp_by_type_balance))

        # check if there is difference between sum of calculated amounts and total_rewards
        total_amount_to_pay = sum(
            [rl.amount for rl in rwrd_logs if not rl.skipped])
        amnt_pay_diff = abs(total_rwrd_amnt - total_amount_to_pay)

        logger.info("Total rewards after processing is {:,} mutez.".format(
            total_rwrd_amnt))
        logger.info(
            "Total amount to pay is {:,} mutez".format(total_amount_to_pay))
        logger.info(
            "Difference between total rewards and total payment amount is {:,} mutez. "
            "This is due to floating point arithmetic. (max allowed diff is {:,})"
            .format(amnt_pay_diff, MINOR_DIFF))

        return rwrd_logs, total_rwrd_amnt