# Path: lsh/utils/similarity.py
# def compute_positive_hash(value):
#     return hash(value) % ((sys.maxsize + 1) * 2)
# 
# Path: lsh/utils/random_number_generator.py
# class RNG(object):
#     """
#         Implements a thread safe, random number generator. It generates a list of n random numbers. By default
#         all numbers are 32bit randomly generated numbers. Users can specify the number of random numbers to be
#         generated.
# 
#         This class is designed such that all threads will have access the same random numbers. This implementation
#         is specific to the needs of our minhash algorithm. In order to implement n number hash functions we use n
#         number random generated numbers and XOR those numbers against hashed values. All documents we process need to
#         use the same random numbers. In order for our LSH implementation to be scalable each thread / instance should
#         have it's own set of generated numbers and this class enables us to do this.
# 
#     """
# 
#     __lock = threading.Lock()
#     __instance = None
#     __random_numbers = None
# 
#     @classmethod
#     def instance(cls, n=1, bits=32):
#         if not cls.__instance and not cls.__random_numbers:
#             with cls.__lock:
#                 if not cls.__instance and not cls.__random_numbers:
#                     cls.__instance = RNG()
#                     cls.__random_numbers = generate_random_numbers(n, bits)
#         return cls.__random_numbers


from operator import xor
from lsh.utils.similarity import compute_positive_hash
from lsh.utils.random_number_generator import RNG
import heapq

    #basic minhash implementation algorithm steps...
    min_hash_values = []

    if shingles_list:
        for shingle in shingles_list:
            # reset min-heap as each shingle should get it's own min-heap
            # to calculate the minimum hash value
            min_heap = []

            for count in range(0, DEFAULT_NUM_RANDOM_NUMS):
                #step 1: calculate hash values for current shingle
                if count == 0:
                    shingle_hash = calc_hash(shingle)
                else:
                    #TODO refactor so that users can specify their own hashing function(s)
                    num = RANDOM_NUMBERS[count]
                    shingle_hash = do_xor(calc_hash(shingle), num)

                #step 2: add to min-heap
                heapq.heappush(min_heap, shingle_hash)

            #step 3: select minimum value from min-heap and add to return list
            min_value = heapq.heappop(min_heap) #note, default behavior for pop gives min value
            min_hash_values.append(min_value)

    return min_hash_values

#TODO refactor so that users can specify their own hashing function(s)
def calc_hash(value):

# Path: lsh/utils/similarity.py
# def compute_positive_hash(value):
#     return hash(value) % ((sys.maxsize + 1) * 2)
# 
# Path: lsh/utils/random_number_generator.py
# class RNG(object):
#     """
#         Implements a thread safe, random number generator. It generates a list of n random numbers. By default
#         all numbers are 32bit randomly generated numbers. Users can specify the number of random numbers to be
#         generated.
# 
#         This class is designed such that all threads will have access the same random numbers. This implementation
#         is specific to the needs of our minhash algorithm. In order to implement n number hash functions we use n
#         number random generated numbers and XOR those numbers against hashed values. All documents we process need to
#         use the same random numbers. In order for our LSH implementation to be scalable each thread / instance should
#         have it's own set of generated numbers and this class enables us to do this.
# 
#     """
# 
#     __lock = threading.Lock()
#     __instance = None
#     __random_numbers = None
# 
#     @classmethod
#     def instance(cls, n=1, bits=32):
#         if not cls.__instance and not cls.__random_numbers:
#             with cls.__lock:
#                 if not cls.__instance and not cls.__random_numbers:
#                     cls.__instance = RNG()
#                     cls.__random_numbers = generate_random_numbers(n, bits)
#         return cls.__random_numbers


# Path: lsh/minhash/minhash.py
from operator import xor
from lsh.utils.similarity import compute_positive_hash
from lsh.utils.random_number_generator import RNG
import heapq


    #basic minhash implementation algorithm steps...
    min_hash_values = []

    if shingles_list:
        for shingle in shingles_list:
            # reset min-heap as each shingle should get it's own min-heap
            # to calculate the minimum hash value
            min_heap = []

            for count in range(0, DEFAULT_NUM_RANDOM_NUMS):
                #step 1: calculate hash values for current shingle
                if count == 0:
                    shingle_hash = calc_hash(shingle)
                else:
                    #TODO refactor so that users can specify their own hashing function(s)
                    num = RANDOM_NUMBERS[count]
                    shingle_hash = do_xor(calc_hash(shingle), num)

                #step 2: add to min-heap
                heapq.heappush(min_heap, shingle_hash)

            #step 3: select minimum value from min-heap and add to return list
            min_value = heapq.heappop(min_heap) #note, default behavior for pop gives min value
            min_hash_values.append(min_value)

    return min_hash_values

#TODO refactor so that users can specify their own hashing function(s)
def calc_hash(value):

     return compute_positive_hash(value)