#!/usr/bin/env python

""" Description: class selects random number from a data set and its probability """


__author__ = "Syed Hamza Rafique"
__copyright__ = "Copyright 2022, MS Test"
__version__ = "1.0.0"
__email__ = "hamzah_shah@hotmail.com"
__status__ = "Production"

from random import choices
import random
import numpy as np
from collections import Counter

class RandSelecter:
            
    @staticmethod    
    def gen_uniform_rand_num(seed_num):
        """ generates random number between 0 and 1 using a uniform distribution"""
        random.seed(seed_num)

        return random.uniform(0, 1)

    @staticmethod
    def select_rand_num(prob_data_set, seed_num):
        """ selects random number from the prob_data_set using associated probababilities"""        

        if type(prob_data_set) is not tuple:
            raise TypeError('Only tuples of tuples allowed as input')

        if not prob_data_set:
            raise TypeError('empty tuple')

            
        numbers = []
        probabillities = []
        rand_num = RandSelecter.gen_uniform_rand_num(seed_num=seed_num)
    
        # test for valid data
        for data in prob_data_set:

            if type(data) is not tuple or len(data) != 2:
                raise TypeError('Only tuples (num, probability) are allowed as inputs')
            
            num, prob = data
            probabillities.append(prob)

            if not 0 <= prob <= 1:
                raise ValueError('probabillities can only be in rand [0:1]')


        if sum(probabillities) != 1:
            raise ValueError('sum of probablities cant be > 1')


        # select num from list based on associated probability
        cumsum = 0   
        for data in prob_data_set:

            num, prob = data            
            cumsum += prob
            if cumsum >= rand_num:
                return [num, prob]

        return [num, prob] 
        


    @staticmethod
    def ref_py_lib_method(prob_data_set, seed_num):
        """python reference function to compare with select_rand_num for correctness """

        if type(prob_data_set) is not tuple:
            raise TypeError('Only tuples')

        if not prob_data_set:
            raise TypeError('empty tuple')

        numbers = []
        probabilities = []

        for data in prob_data_set:

            if type(data) is not tuple or len(data) != 2:
                raise TypeError('Only tuples (num, probability) are allowed as inputs')
            
            num, prob = data

            if not 0 <= prob <= 1:
                raise ValueError('probabilities can only be in rand [0:1]')


            numbers.append(num)
            probabilities.append(prob)

        if sum(probabilities) != 1:
            raise ValueError('sum of probablities cant be > 1')

        random.seed(seed_num)
        item = choices(numbers, probabilities)
        index = numbers.index(item[0])

   
        return  [item[0], probabilities[index]];